細說koa-static使用
在app.js裡,若想指定目前目錄為托管目錄,我們一般會這樣做:
const static=require('koa-static')
const Koa=require('koa')
const app=new Koa()
app.use(static('.'))
app.listen(8081)
koa-static 就是koa(node架構)中最常用的、較為成熟的 靜态web托管服務中間件 ,在koa中常用于比如外鍊靜态資源(如CSS檔案):
//下載下傳
npm install koa-static --save
//引入
const server=require('koa-static')
//使用
app.use(server('static'))
//或:app.use(server(__dirname+'/static'))
總之,server裡面一定是靜态模闆(相對)路徑
然後我們就可以這樣使用static目錄下的css檔案夾中的xxx.css檔案了:
<link rel="stylesheet" href="css/xxx.css" />
這麼簡單?那它的原理是啥?
根據檔案字尾名設定請求頭 「Content-Type」值,使之與浏覽器渲染相比對!
就拿上面說的static說吧:
- 尋找
是否存在static/css/xxx.css
- (若存在)設定
Content-Type: text/css;charset=utf-8;
- 通過response傳回給浏覽器
前面說了koa-static作用是 ☞靜态檔案托管☜ ,那肯定不隻是對于CSS、JavaScript這類資源檔案。
事實上,對于圖檔,koa-static同樣可以用于設定 圖檔緩存 !就像這樣
const server=require('koa-static')
const path=require('path') //path子產品:設定路徑資訊
const staticPath=path.resolve(__dirname,'static')
const staticServer=server(staticPath,{
setHeadears:(res,path,stats)=>{
if(path.indexOf(/[jpg|png|gif|jpeg]/)>-1){
res.setHeader('Cache-Control',['private','max-age=60'])
}
}
})
app.use(staticServer);
——如果對應路徑中是jpg/GIF/png/jpeg格式的圖檔,那麼就将其緩存60s。
我們都知道,在express(node架構)中有一個關于靜态服務的“便捷方式”:
app.use('/teacher',express.static('/public'))
它可以指定靜态服務的“請求字首” —— 就是指定加載相對于哪個url的靜态資源。
很明顯,這是非常實用的。我們突然想到,本文上面我們所說的koa-static都是相對于“全局 ”作用的?
如何在koa中實作這個功能呢?koa為開發者提供了另一個(輔助)子產品 —— koa-mount
const Koa=require('koa')
const server=require('koa-static')
const mount=require('koa-mount')
const app=new Koa()
app.use(mount('/teacher',server('/public')))
koa-mount是一個将中間件挂載到指定路徑的Koa中間件。它可以挂載任意Koa中間件!
前面說過,koa-static是一個中間件,是以koa-mount可以和koa-static結合,以實作和express一樣的靜态服務請求字首的功能。
static原理探究
學習了上面神奇的使用方式,你有沒有想過它是怎麼實作的?
通過
npm info koa-static
,你會發現 koa-static 依賴兩個子產品,分别是 debug 和 koa-send 。
找到 koa-static 源碼的index檔案,其核心實作如下:
const send = require('koa-send');
//...
function serve (root, opts) {
//...
return async function serve (ctx, next) {
await next()
if (ctx.method !== 'HEAD' && ctx.method !== 'GET') return
if (ctx.body !== null && ctx.status !== 404) return // eslint-disable-line
try {
await send(ctx, ctx.path, opts)
}catch (err) {
if (err.status !== 404) {
throw err
}
}
}
}
而經過這段代碼,我們發現其中實作核心是
send()
方法,而這是由子產品 koa-send
找到koa-send的源碼,發現其核心實作原理也是很簡單的:
if (!ctx.type) ctx.type = type(path, encodingExt)
ctx.body = fs.createReadStream(path)
其中type方法是根據檔案字尾來設定 Content-Type !很實用,但是我們這裡更要關注的是另一個比較有趣的事 —— koa-send的原理:
- 設定 Content-Type ,可通過檔案字尾進行設定;
- 以Stream形式為ctx.body指派
為什麼說它有趣呢?
除了它竟然也是以設定content-type為目标外,stream流的方式一直受到業界大拿們的推崇:因為它比
fs.readFileSync
更高效!
讓我們拿下面這段代碼和上面 koa-send 的源碼作比較:
app.use(function(ctx){
const fs=require('fs')
const result=fs.readFileSync('xxx')
ctx.type=type(result, encodingExt)
ctx.body=result
})
Koa回顧
- 根據body類型設定對應的Content-type
- 根據Content-type調用res.write或者res.end,将資料寫入浏覽器