超輕量級web框架koa源碼閱讀

koa是一個很是輕量的web框架,裏面除了ctx和middleware以外什麼都沒有,甚至連最基本的router功能都須要經過安裝其餘中間件來實現。不過雖然簡單,可是它卻很是強大,僅僅依靠中間件機制就能夠構建完整的web服務。而koa的源碼一樣很簡潔,基礎代碼只有不到2000行,很是適合閱讀學習。node

koa的源碼直接從github獲取,本文采用目前最新的2.5.1版本。git

代碼結構

第一眼看到koa的源碼時候我真的懵了,反覆確認沒有看錯以後才確信,koa源碼只有四個文件--application.js,context.js,request.js,response.js,位於項目的lib文件夾下。並且一看文件名基本上就能猜到每一個文件是作什麼的了,接下來就是打開查看裏面的內容。github

koa基本啓動流程

首先看package.json裏面的main,能夠知道application.js是入口文件,裏面是一個繼承自event模塊下的Emitter類的Application類,咱們使用koa時候建立的app實例就是在這裏定義的。web

分析一個類天然要先看它的構造函數,裏面重點的就是定義了一個數組middleware,還有三個屬性context,request,response分別爲三個對象,而這三個對象就是在對應的其餘三個文件中定義的。在此咱們先不看另外的文件,想一想咱們使用koa的時候,建立app實例以後,接下來就是use各類中間件了,因此直接看use方法。json

use接收一箇中間件函數做爲參數,首先作類型校驗,若是傳入的是generator,在koa2中會先經過convert進行轉換(此處是爲了兼容koa1,後續版本將移除),最後其實只作了一件事,就是把這個函數push到middleware數組中去。use方法最後會返回this,也就是koa實例自己,這就意味着咱們能夠實現鏈式調用。數組

設置好中間件,咱們開啓koa服務的最後一步就是調用listen方法設置監聽端口,接下來就看一下listen方法的實現。咱們會發現listen更簡單,只有兩行,其實什麼額外的事情也沒作,只是調用了node原生的http模塊下面的createServer方法建立服務,listen方法設置監聽,僅此而已。咱們都知道http的createServer須要傳入一個函數,這個函數在koa裏面是經過調用callback方法返回的,接下來看callback的實現。promise

callback裏面首先使用compose把全部的中間件變成一個函數(compose的實現一樣後續會詳細分析),這裏會首先調用Emitter中的listenerCount方法判斷是否有error事件的監聽器,若是沒有會爲error事件註冊默認的事件監聽方法onerror,以後就是定義咱們要的那個傳入createServer的函數了。這個函數接收req和res兩個參數,以後,koa會對其作一個處理:經過調用createContext方法把req和res封裝成咱們熟悉的ctx對象(createContext具體作了哪些工做接下來會說),而後把ctx和以前處理好的中間件函數fnMiddleware傳入handleRequest方法中。app

handleRequest中首先先取出res,先把狀態置爲404,而後對執行中間件後的成功和失敗狀態註冊方法,失敗調用ctx.onerror捕獲異常,成功調用respond方法處理結果。這裏仍是用了onFinished模塊,onFinished能確保一個流在關閉、完成和報錯時都會執行相應的回調函數,這裏把咱們的異常處理函數傳入用以處理錯誤信息。而respond方法,裏面作的,就是讀取ctx信息,把數據寫入res中並響應請求。至此,整個流程就完成了。框架

ctx的建立

createContext裏面的代碼其實特別簡單,就是建立了三個對象context,request,response,而後把使用ctx時候的各類東西都掛到context對象上,這樣咱們就能夠在ctx上面獲取到req,res等等各類信息了。建立context,request,response對象時候用到了當前app類裏面的三個對象,它們是經過從外部三個文件中引入的對象來建立的,因此接下來就看一下這三個文件中都有什麼。koa

這三個文件導出的都是對象,在context中,只作了一些基礎方法的定義,剩下的一切屬性方法所有都使用delegate代理到request和response屬性的訪問了。而前面咱們已經知道,context上面的request和response就是經過另外的兩個文件中的對象建立獲得的。而這兩個文件的內容就更加簡潔了,都是咱們平時使用時候訪問的屬性和方法,經過getter和setter的方式來控制上面的req和res從而實現對實際請求和響應操做的封裝。因而整個koa核心的四個文件就完全完成了。

compose實現原理與中間件機制

首先作一些合法性校驗,重點在於最後的返回結果是一個函數,這個函數就是咱們上面的fnMiddleware,它一樣也有context和next兩個參數,在其內部採用index變量記錄當前處理到哪一個中間件,而後從第一個開始調用dispatch方法。首先會判斷當前傳入參數與index的關係,若是在一箇中間件內屢次調用next,會出現參數小於index的狀況,此時就會報錯。以後把當前中間件從數組中取出來,每次執行時會把ctx和next傳入,next中調用dispatch,參數爲下一個位置,這樣就會按順序把中間件添加進來,最後當i等於中間件數組長度時候,也就是沒有其餘中間件了,那麼執行一開始傳入的next參數,若是fn不存在,返回空的promise。當中心執行完,也就是前一箇中間件的next執行完,天然會觸發await向下執行,以後執行權會反向順序返回,最終組合的結果就是先從外向裏,再從裏向外,就是咱們熟知的洋蔥圈模型。

錯誤處理機制

koa的錯誤處理機制也頗有特色,咱們只要監聽koa實例的error事件,就能夠統一處理全部的錯誤。咱們在前面提到過,調用fnMiddleware失敗後會被統一的onerror方法捕獲,這個方法是對應到ctx上的onerror方法,咱們來看一下里面的實現,裏面很是重要的一行就是this.app.emit('error', err, this);,因爲咱們的koa是繼承自event,因此能夠派發出一個error事件,咱們只要處理該事件便可。而前面在中間件處理中,若是發生錯誤就會reject,天然能夠被catch捕獲到。

以上,就是koa基本核心模塊的流程,原理很簡單,可是配合各類中間件,koa徹底能夠實現一個功能完整web server。

本文原創,願意分享但轉載請提早告知,更多文章查看個人主頁,感謝閱讀,如錯誤歡迎指正。

相關文章
相關標籤/搜索