查看Koa,version@2.7源碼,總共只有四個文件application.js
、context.js
、request.js
、response.js
;分別對應Koa應用入口、上下文環境、請求對象和響應對象
能夠看到這個模塊以Node內部模塊events
爲父類,導出一個建立Koa應用的構造函數,這個構造函數中有幾個屬性值,咱們主要看javascript
咱們使用中通常只傳入一個number,讓服務啓動在某個端口號。能夠看到listen內部調用的是node建立httpserver的方法。這裏主要看http.createServer
方法的接受一個請求的監聽函數,同時這個函數的兩個參數分別爲req
請求對象和res
響應對象;
查看callback
方法,java
callback() { const fn = compose(this.middleware); if (!this.listenerCount('error')) this.on('error', this.onerror); const handleRequest = (req, res) => { const ctx = this.createContext(req, res); return this.handleRequest(ctx, fn); }; return handleRequest; }
開始經過compose
返回鏈式處理中間件的函數,這裏查看componse方法是如何封裝的。首先先校驗了中間件數組是否符合要求,而後在返回的函數中能夠看到,經過promise.resolve
將返回中間件的執行結果,中間件第一個參數是ctx
,第二個參數是next
,這裏能夠看到dispatch
就是對應next,當咱們執行該方法就會將中間件繼續遞歸進行調用。這樣就造成Koa中那種洋蔥模型的執行順序的一個promise。node
//componse方法 function compose (middleware) { if (!Array.isArray(middleware)) throw new TypeError('Middleware stack must be an array!') for (const fn of middleware) { if (typeof fn !== 'function') throw new TypeError('Middleware must be composed of functions!') } /** * @param {Object} context * @return {Promise} * @api public */ return function (context, next) { // last called middleware # let index = -1 return dispatch(0) function dispatch (i) { if (i <= index) return Promise.reject(new Error('next() called multiple times')) index = i let fn = middleware[i] if (i === middleware.length) fn = next if (!fn) return Promise.resolve() try { return Promise.resolve(fn(context, dispatch.bind(null, i + 1))); } catch (err) { return Promise.reject(err) } } } }
而後在看handleRequest
,這裏開始有點迷惑到我,他在callback
裏面定義了一個函數也叫handleRequest,但後面這個只是一個函數變量的名字,裏面的handleRequest
纔是咱們要分析的。爲了方便說明查看,能夠臨時改寫成如下這種寫法。api
callback() { const fn = compose(this.middleware); if (!this.listenerCount('error')) this.on('error', this.onerror); const xxx = (req, res) => { const ctx = this.createContext(req, res); return this.handleRequest(ctx, fn); }; return xxx; }
在xxx函數中,首先使用createContext
方法,將請求報文、響應報文等對象掛載在上下文對象上, 其中request
是Koa請求對象,response
是Koa響應對象,app
爲當前Koa服務實例對象,req
爲Node的請求對象,res
爲Node響應對象。而後執行handleRequest
方法,參數爲上下文和處理中間件的函數。最後返回一次請求的執行結果。這裏能夠看到返回狀態碼默認爲404數組
createContext(req, res) { const context = Object.create(this.context); const request = context.request = Object.create(this.request); const response = context.response = Object.create(this.response); context.app = request.app = response.app = this; context.req = request.req = response.req = req; context.res = request.res = response.res = res; request.ctx = response.ctx = context; request.response = response; response.request = request; context.originalUrl = request.originalUrl = req.url; context.state = {}; return context; }
handleRequest(ctx, fnMiddleware) { const res = ctx.res; res.statusCode = 404; const onerror = err => ctx.onerror(err); const handleResponse = () => respond(ctx); onFinished(res, onerror); return fnMiddleware(ctx).then(handleResponse).catch(onerror); }
如上callback中使用的兩個方法promise
use方法很簡單,就是將單箇中間件push進數組保存cookie
toJSON方法引入一個only的包,打開能夠看到就是將一個對象過濾返回出一個新的只包含指定屬性值的對象app
打印錯誤日誌函數
處理響應結果對象,涉及了一些buffer和stream的概念,最後返回響應結果this
返回就是上下文環境的對象原型
返回當前對象
返回請求報文和響應報文和其餘一些屬性的對象
異常處理
cookies取值和賦值的函數
從上面看到當前上下文原型的屬性、方法還不多,後面主要是經過delegate
定義其餘屬性方法。查看delegate
依賴包
function Delegator(proto, target) { if (!(this instanceof Delegator)) return new Delegator(proto, target); this.proto = proto; this.target = target; this.methods = []; this.getters = []; this.setters = []; this.fluents = []; }
返回一個以Delegator
爲構造函數的對象,返回的對象定義了上面幾個屬性proto
參數就是傳入的上下文原型對象,target
由於這個Delegator
函數調用了兩次分別是response
和request
而後Delegator
原型上有幾個方法method
、access
、getter
、setter
、fluent
,調用之後都把原對象返回了,因此在context.js
中使用時能夠鏈式調用。以method
方法爲例子:
Delegator.prototype.method = function(name){ var proto = this.proto; var target = this.target; this.methods.push(name); proto[name] = function(){ return this[target][name].apply(this[target], arguments); }; return this; };
調用該方法,首先將方法名保存進方法集合數組,而後在原對象上定義屬性方法,這時的proto
就是上下文原型對象,而後target參數傳遞表示該方法具體是用的response
仍是request
對象上的方法。
總結就是上下文原型對象就是定義了response
、request
等這些重要屬性,同時將這兩個對象中的方法,快捷的直接綁定在原型對象上,因此有些屬性或方法使用時能夠省略response和request。
定義一個request對象,將Node請求報文req
的一些屬性和方法掛載該對象上並返回
和request相似,返回一個response對象,再回顧下application.js
中的createContext
方法;
其中request
是Koa請求對象,response
是Koa響應對象,app
爲當前Koa服務實例對象,req
爲Node的請求對象,res
爲Node響應對象