本筆記共四篇
Koa源碼閱讀筆記(1) -- co
Koa源碼閱讀筆記(2) -- compose
Koa源碼閱讀筆記(3) -- 服務器の啓動與請求處理
Koa源碼閱讀筆記(4) -- ctx對象javascript
自從寫了個Koa的腳手架koa2-easy,愈發以爲Koa的精妙。
因而抱着知其然也要知其因此然的想法,開始閱讀Koa的源代碼。前端
讀Koa源代碼時,天然是帶着諸多問題的。不管是上一篇所寫的generator
函數如何自動執行,仍是對於Koa中間件如何加載,next參數如何來的。都充滿了好奇。
今天寫文章,並非介紹整個koa-compose
如何如何(涉及太寬,準備放在下面幾篇統一介紹)。而是從自身需求出發,找到問題的答案。
而問題就是Koa中間件的加載,和next參數的來源。java
首先的是Koa加載初始化時的函數(刪除部分):git
// Koa類 function Application() { this.middleware = []; } // Koa原型 var app = Application.prototype; // Koa中間件加載函數 app.use = function(fn){ if (!this.experimental) { // es7 async functions are not allowed, // so we have to make sure that `fn` is a generator function assert(fn && 'GeneratorFunction' == fn.constructor.name, 'app.use() requires a generator function'); } this.middleware.push(fn); return this; };
在這兒不難看出,Koa對象內部有個中間件的數組,其中全部中間件都會存在其中。
而在服務器啓動時,則會調用並處理該數組。
源代碼以下:github
var co = require('co'); var compose = require('koa-compose'); var fn = co.wrap(compose(this.middleware))
在fn被處理完後,每當有新請求,便會調用fn,去處理請求。
而在這裏,co.wrap的做用是返回一個Promise
函數,用於後續自動執行generator
函數。segmentfault
因而不難看出,中間件這兒的重點,是compose函數。
而compose函數的源代碼雖然很簡潔,可是也很燒腦。(對我而言)api
/** * Compose `middleware` returning * a fully valid middleware comprised * of all those which are passed. * * @param {Array} middleware * @return {Function} * @api public */ // 傳入中間件做爲參數 function compose(middleware){ return function *(next){ // next不存在時,調用一個空的generator函數 if (!next) next = noop(); var i = middleware.length; // 倒序處理中間件,給每一箇中間件傳入next參數 // 而next則是下一個中間件 while (i--) { next = middleware[i].call(this, next); } return yield *next; } } function *noop(){}
在這裏,得提一提Koa
中間件的調用方式。數組
app.use(function * (next) { this.set('Koa', 'Example'); yield next; }) app.use(function * (next) { this.body = 'Hello World' })
在中間件中的next,則是在koa-compose
中傳入的。
而這兒, yield next
和 yield *next
也是有區別的。yield next
, next 會做爲next()的value返回。
而yield *next
則是在generator
函數內執行這個generator
函數。服務器
這兩天一直在讀Koa的源代碼,細細看來不是很難,可是被做者的奇思妙想給打動了。
接下來會繼續寫一些閱讀筆記,由於看Koa的源代碼確實是獲益匪淺。app
前端路漫漫,且行且歌
最後附上本人博客地址和原文連接,但願能與各位多多交流。