Koa2原理詳解

ArtJCGL 8年前發布 | 28K 次閱讀 Koa.js 中間件 Koa Web框架

1. Koa vs Express

Koa 是繼 Express 之后,Node的又一主流Web開發框架。相比于Express,Koa只保留了核心的中間件處理邏輯,去掉了路由,模板,以及其他一些功能。

另一方面,在中間件的處理過程中,Koa和Express也有著一定區別,看下面例子:

// http style
http.createServer((req, res) => {
  // ...
})

// express style app.use((req, res, next) => { // ... })

// koa style app.use((ctx, next) => { // ... })</code></pre>

Node自帶的 http 模塊處理請求的時候,參數是一個 req 和 res ,分別為 http.IncomingMessage 和 http.ServerResponse 的實例。

Express對請求參數 req 和 res 的原型鏈進行了擴展,增強了 req 和 res 的行為。

而Koa并沒有改變 req 和 res ,而是通過 req 和 res 封裝了一個 ctx (context) 對象,進行后面的邏輯處理。

2. Koa基本組成

Koa源碼非常精簡,只有四個文件:

  • application.js :Application(或Koa)負責管理中間件,以及處理請求
  • context.js :Context維護了一個請求的上下文環境
  • request.js :Request對 req 做了抽象和封裝
  • response.js :Response對 res 做了抽象和封裝

3. Application

Application主要維護了中間件以及其它一些環境:

// application.js
module.exports = class Application extends Emitter {
  constructor() {
    super();

this.proxy = false;
this.middleware = [];
this.subdomainOffset = 2;
this.env = process.env.NODE_ENV || 'development';
this.context = Object.create(context);
this.request = Object.create(request);
this.response = Object.create(response);

}

// ... }</code></pre>

通過 app.use(fn) 可以將 fn 添加到中間件列表 this.middleware 中。

app.listen 方法源碼如下:

// application.js
listen() {
  debug('listen');
  const server = http.createServer(this.callback());
  return server.listen.apply(server, arguments);
}

首先會通過 this.callback 方法來返回一個函數作為 http.createServer 的回調函數,然后進行監聽。我們已經知道, http.createServer 的回調函數接收兩個參數: req 和 res ,下面來看 this.callback 的實現:

// application.js
callback() {
  const fn = compose(this.middleware);

if (!this.listeners('error').length) this.on('error', this.onerror);

return (req, res) => { res.statusCode = 404; const ctx = this.createContext(req, res); onFinished(res, ctx.onerror); fn(ctx).then(() => respond(ctx)).catch(ctx.onerror); }; }</code></pre>

首先是將所有的中間件通過 compose 組合成一個函數 fn ,然后返回 http.createServer 所需要的回調函數。于是我們可以看到,當服務器收到一個請求的時候,會使用 req 和 res 通過 this.createContext 方法來創建一個上下文環境 ctx ,然后使用 fn 來進行中間件的邏輯處理。

4. Context

通過上面的分析,我們已經可以大概得知Koa處理請求的過程:當請求到來的時候,會通過 req 和 res 來創建一個 context (ctx) ,然后執行中間件。

事實上,在創建 context 的時候,還會同時創建 request 和 response ,通過下圖可以比較直觀地看到所有這些對象之間的關系。

圖中:

  • 最左邊一列表示每個文件的導出對象
  • 中間一列表示每個Koa應用及其維護的屬性
  • 右邊兩列表示對應每個請求所維護的一些列對象
  • 黑色的線表示實例化
  • 紅色的線表示原型鏈
  • 藍色的線表示屬性

實際上, ctx 主要的功能是代理 request 和 response 的功能,提供了對 request 和 response 對象的便捷訪問能力。在源碼中,我們可以看到:

// context.js
delegate(proto, 'response')
  .method('attachment')
  // ...
  .access('status')
  // ...
  .getter('writable');

delegate(proto, 'request') .method('acceptsLanguages') // ... .access('querystring') // ... .getter('ip');</code></pre>

這里使用了 delegates 模塊來實現屬性訪問的代理。

簡單來說,通過 delegate(proto, 'response') ,當訪問 proto 的代理屬性的時候,實際上是在訪問 proto.response 的對應屬性。

5. Request & Response

Request對 req 進行了抽象和封裝,其中對于請求的url相關的處理如圖:

┌────────────────────────────────────────────────────────┐
│                           href                         │
├────────────────────────────┬───────────────────────────┤
│          origin            │     url / originalurl     │
├──────────┬─────────────────┼──────────┬────────────────┤
│ protocol │      host       │   path   │     search     │
├──────────├──────────┬──────┼──────────┼─┬──────────────┤
│          │ hostname │ port │          │?│ querystring  │
│          ├──────────┼──────┤          ├─┼──────────────┤
│          │          │      │          │ │              │
"  http:   │ host.com : 8080   /p/a/t/h  ?  query=string │
│          │          │      │          │ │              │
└──────────┴──────────┴──────┴──────────┴─┴──────────────┘

Response對 res 進行了封裝和抽象,這里不做贅述。

 

來自:http://syaning.com/2016/11/08/koa2/

 

 本文由用戶 ArtJCGL 自行上傳分享,僅供網友學習交流。所有權歸原作者,若您的權利被侵害,請聯系管理員。
 轉載本站原創文章,請注明出處,并保留原始鏈接、圖片水印。
 本站是一個以用戶分享為主的開源技術平臺,歡迎各類分享!