目標
本文主要通過一個簡單的例子來解釋koa的內部原理。
koa的一個簡單例子圖1是Koa的一個簡單例子,下文會對這個例子的每行程式碼背後的邏輯做詳細分析。
koa內部檔案組成圖2 Koa程式碼檔案
圖3 檔案和具體類
application.js中包含了Application類和一些輔助方法context.js主要作用是承載上下資訊,並封裝了處理上下文資訊的操作request.js中封裝了處理請求資訊的基本操作response.js中封裝了處理響應資訊的基本操作koa內部
在圖1的例子中,執行const app = new koa()時,實際上構造了一個Application例項,圖4為Application構造方法,構造方法中建立了Context、Request、Response等類的執行例項。關於Context、Response、Request類及方法介紹請參考
https://koajs.com/
下文約定request代指Request類例項,response代指Response類例項,context代指Context類例項。
圖4 Application構造方法
圖1的例子中呼叫app.listen(3000)方法監聽3000埠進來的http請求,listen方法內部建立了一個http.Server物件,並呼叫http.Server的listen方法。具體程式碼如圖5。
圖5中this.callback方法的原始碼如下圖6中所示
圖6 callback
callback方法返回一個handleRequest函式,作為createServer的引數,當http.Server例項接收到一個http請求時,會將請求資訊和請求響應物件傳給handleRequest函式,具體指將http.IncomingMessage的例項req,和http.ServerResponse的例項res傳給handleRequest函式。其中this.createContext函式的原始碼如下圖7
http.IncomingMessage和http.ServerResponse資訊可以參照node.js官網
圖7 createContext
圖7中createContext的主要作用是將請求資訊和響應資訊封裝在Request和Response類的執行例項中,並建立上下文類Context例項。context物件包含了Application、Request、Response等例項的引用。在this.callback方法中還有另一行很重要的程式碼,如下程式碼片段1。
程式碼片段1
const fn = compose(this.middleware);可以從圖4中Application的構造方法中知道this.middleware是一個數組,該陣列用來儲存app.use方法傳入的中介軟體函式。Application的use方法具體程式碼實現細節如圖8,其中最關鍵的一行程式碼是this.middleware.push(fn)。
compose方法的程式碼實現包含在koa-compose包中,具體程式碼實現細節如圖9
圖9 compose
compose方法接收this.middleware陣列,返回一個匿名函式,該函式接收兩個引數,上下文例項context和一個next函式,執行該匿名函式會執行this.middleware陣列中的所有中介軟體函式,然後在執行傳入的next函式。匿名函式呼叫是在Application的callback方法中,在圖6的callback方法的最後將執行上下文物件和compose方法返回的匿名函式作為引數傳入this.handleRequest方法。接下來看一下this.handleRequest方法的具體細節,如圖9。
在this.handleRequest方法中建立了錯誤處理方法onError和返回響應的方法handleResponse。fnMiddleware就是compose方法返回的匿名函式。在this.handleRequest方法的最後執行匿名函式,並傳入hanldeResponse和onError函式分別處理正常請求響應流程和異常情況。
return fnMiddleware(ctx).then(handleResponse).catch(onerror);執行fnMiddleware函式,實際上是執行之前傳入所有中介軟體函式。在中間函式中可以拿到上下文物件的引用,通過上下文物件我們可以獲取到經過封裝的請求和響應例項,具體形式如圖10。
圖10 中間間函式例子
在中介軟體方法中可以設定響應頭資訊、響應內容。以及讀取資料庫,獲取html模版等。將需要返回給使用者端的資料賦值給上下文物件context的body屬性。respond方法的具體實現如圖11。
圖11 respond
respond方法的主要作用是對返回的內容進行一些處理,然後呼叫node.js的http.ServerResponse例項的end方法,將具體內容返回給使用者端。
request.js和response.js在Application的createContext方法中,將node.js的請求(http.IncomingMessage)和響應物件(http.ServerResponse)分別賦值給了Request類和Response類例項物件。Request中主要包含了處理請求的方法(實際上都是get方法,或者獲取器),獲取請求資料,例子如圖12。
上面的程式碼中this.req是http.IncomingMessage例項,包含了http請求資訊。Response中包含了處理請求響應的操作。例如設定響應狀態資訊,如圖13
其中this.res指http.ServerResponse物件例項。
到此處,圖1中三行程式碼的背後邏輯已分析完成。