koa 發佈已經快 6 年的時間,做爲繼 express 以後 node 服務框架最大的黑馬,有不少的設計思想值得咱們學習,本文從簡到繁逐步介紹 koa,同時適合新老手閱讀。html
這裏引用中文官方網站的原文前端
Koa 是一個新的 web 框架,由 Express 幕後的原班人馬打造, 致力於成爲 web 應用和 API 開發領域中的一個更小、更富有表現力、更健壯的基石。 經過利用 async 函數,Koa 幫你丟棄回調函數,並有力地加強錯誤處理。 Koa 並無捆綁任何中間件, 而是提供了一套優雅的方法,幫助您快速而愉快地編寫服務端應用程序。vue
既然是 web 框架你們必定不陌生,經過啓動一個 node http server,監聽一個端口,進而咱們就能夠經過相似 localhost:3000
在本地訪問咱們的服務了,這個服務能夠是 web 網站,能夠是 restful 接口,也能夠是靜態文件服務等等。node
任何語言、框架都存在 Hello Word
示例,來表達其最簡單的入門 Demo,代碼以下web
此時訪問瀏覽器 localhost:3000
,咱們會看到打印出了 Hello Word
,此時一個基於 koa 的服務就啓動完成了。express
理解 koa 第一步,搞清楚上下文的做用api
例如:微信羣裏面有人說外面下雪了,你跑到窗邊看到的倒是晴空萬里,這時你才意識到一樣是 10 月份,他在寒冷的北方,你在酷暑的南方。數組
相似的,一次請求會包含用戶的登陸狀態,或者一些Token之類的信息,這些信息就是上下文的一部分,用於肯定一次的請求環境。瀏覽器
Koa 的 Context 把 node 的 request, response 對象封裝進一個單獨對象, 並提供許多開發 web 應用和 APIs 有用的方法. 那些在 HTTP server 開發中使用很是頻繁操做, 直接在 Koa 裏實現, 而不是放在更高層次的框架, 這樣中間件就不須要重複實現這些通用的功能。bash
先來看一個官方的例子:
簡單解釋下,代碼起始初始化一個 koa 實例,下面分別經過 use 方法載入了三個中間件方法,執行順序:
next()
跳到下一個中間件new Data()
記錄當前時間next()
跳到下一個中間件ctx.body
賦值http header
中X-Response-time
打印出來這裏的執行順序延伸出了十分經典的洋蔥模型
在一次請求的過程當中會往返通過同一中間件兩次,容許咱們處理不一樣請求階段的邏輯。
上面分別介紹了 koa 裏面兩個最重要的概念,下面咱們分析下 koa 內部是如何運做的,所謂的洋蔥模型是如何創建的
koa 源碼的 lib 目錄十分簡單。
lib
|- application.js
|- context.js
|- request.js
|- response.js
複製代碼
入口文件是 application.js
,咱們先從這裏入手
Application 是一個 class,這個類繼承了 node 的 Events 這裏不詳細展開,在 constructor
中初始化了如下內容:
NODE_ENV
來判斷。Object.create
方法將 lib 目錄下對應的文件導入到 this
當前上下文,且不污染引入對象。按照正常的編碼順序,在初始化完 koa 實例後(即 const app = new Koa()
),咱們須要調用 app.use()
去掛載咱們的中間件,那麼咱們看下 use 方法作了什麼
判斷中間件爲 function,判斷中間件是否爲 generator function 類型,只是簡單的將中間件函數 push
到了 middleware
數組中。
此時心中有沒有大寫的 WHAT?
其實就是這麼直白,沒什麼複雜邏輯,後面也許你們都猜到了,循環調用 middleware
中的方法去執行,此處還沒有代表洋蔥模型是怎麼來的,咱們先不展開,繼續按代碼順序執行。
按照正常的編碼順序,在 use
完咱們的中間件以後就是 app.listen(3000)
一塊兒看下這個 listen 幹了什麼
這裏的 http.createServer
就是 node 原生啓動 http 服務的方法,這裏稍微擴展下基礎知識,此方法接受兩個參數
options[IncomingMessage, ServerResponse]
這裏從 node 版本 v9.6.0, v8.12.0 後才支持,這裏不贅述requestListener
此參數爲 function 類型,每次請求會傳入 req, res
兩個參數不難理解這裏的 this.callback()
方法必定是返回了一個函數,而且接收兩個參數 (req, res)
,下面看下源碼
這個 callback 中的信息量有點大,代碼自己並不難理解,註釋也有說明,從這裏展開從上到下分別解釋。
這裏的 compose 方法主要負責生成洋蔥模型,經過 koa-compose
包實現,源碼以下
從註釋看得出大體邏輯,這裏的巧妙之處在於 fn(context, dispatch.bind(null, i + 1))
,
這個 dispatch.bind(null, i + 1)
就是咱們一般寫中間件的第二個參數 next
,
咱們執行這個 next()
方法實際上獲得的是下一個中間件的執行,
也就不難理解爲何咱們 await next()
。 的時候等待的是後面全部中間件串聯執行後了,回頭再看下上文中間件部分的執行順序就豁然開朗了。
callback 中的展開解釋,看下 const ctx = this.createContext(req, res)
作了什麼
這裏主要是將 req, res 及 this.request, this.response 都掛載到了 context。 上,並經過賦值理清了循環引用層級關係,爲使用者提供方便。
仍是 callback 中的展開解釋,看下 this.handleRequest(ctx, fn)
這部分作了什麼
分別拿到 ctx 和 compose 生成的洋蔥模型,開始逐一消費中間件。
上面理清了總體框架,下面看下 context.js
內部的細節,在文件結尾有兩大段的代理。
這裏能夠看到全部的 req 及 res 的方法集合,那麼哪些方法可讀,哪些可寫,哪些既可讀又可寫,哪些方法不容許修改
這就是 delegates
這個庫作的事情。
delegates
內部利用了,__defineGetter__
和 __defineSetter__
方法控制讀寫,固然咱們能夠從中學習思想,也不能盲從。
這兩個 api 去 MDN 上搜索會給出相同的警告信息
This feature is deprecated in favor of defining setters using the object initializer syntax or the Object.defineProperty() API.
其實仍是建議咱們使用 vue 的代理方式 Object.defineProperty()
,不過這個庫有四年沒更新了依然穩定運行着,仍是深受 koa 開發者承認的。
request.js
和 response.js
文件沒什麼能夠講,就是具體的工具方法實現,方便開發人員調用,感興趣能夠自行閱讀源碼。
智聯前端架構總體的 node 服務都基於 koa 實現,包括咱們的 vue 服務端渲染和 node restful api 等等。
咱們選擇 koa 的緣由是其自己輕巧,可擴展性良好,支持 async、await 的異步,完全擺脫了回調地獄。
市面上也有成熟基於 koa2 的企業級解決方案,如 eggjs 和 thinkjs。
揭開 koa 的神祕面紗,讓開發者關注業務邏輯同時也關注下框架自己,有利於問題排查和編寫擴展,與此同時能夠學習 express、hapi 等同類型框架的思想,結合現有企業級解決方案,選一款適合你的框架,總之框架不論好壞,只論場景。