天天看點

對靜态檔案中間件koa-static的一些了解

細說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說吧:

  1. 尋找​

    ​static/css/xxx.css​

    ​ 是否存在
  2. (若存在)設定​

    ​Content-Type: text/css;charset=utf-8;​

  3. 通過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的原理:

  1. 設定 Content-Type ,可通過檔案字尾進行設定;
  2. 以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回顧

  1. 根據body類型設定對應的Content-type
  2. 根據Content-type調用res.write或者res.end,将資料寫入浏覽器

繼續閱讀