大白話講解 Koa2 洋蔥模型

做者: gauseen
原文: https://github.com/gauseen/blog

Koa.js

Koa.js 是一個極其精簡的 Web 服務框架,主要提供如下功能:前端

  • HTTP 服務:主要處理 requestresponse
  • 中間件數據處理機制(洋蔥模型)

什麼是 AOP?

AOP 爲 Aspect Oriented Programming 的縮寫,中文意思爲:面向切面編程,它是函數式編程的一種衍生範式git

舉個栗子 github

假如我想把一個蘋果(源數據)處理成果盤(最終數據)我該怎麼作?編程

① 蘋果(源數據) ---->
② 洗蘋果 ---->
③ 切蘋果 ---->
④ 放入盤子 ---->
⑤ 果盤(最終數據)設計模式

共有 5 個步驟,若是我想升級一下果盤,打算在切蘋果以前先削皮,放入盤子後擺成五角星形狀那麼個人步驟應該以下:瀏覽器

① 蘋果(源數據) ---->
② 洗蘋果 ---->
③ 削皮 ---->
④ 切蘋果 ---->
⑤ 放入盤子 ---->
⑥ 擺成五角星形狀 ---->
⑦ 果盤(最終數據)app

上面每一個步驟均可以當作相應的方法,步驟 ③ 和 ⑥ 加入與否都不影響我製做出果盤這個結果,能夠看出這樣是很是靈活的框架

其實這就是生活中面向切面編程的例子,
換句話說,就是在現有程序中,加入或減去一些功能不影響原有的代碼功能。koa

什麼是 Koa.js 洋蔥模型?

洋蔥模型其實就是中間件處理的流程,中間件生命週期大體有:async

  • 前期處理
  • 交給並等待其它中間件處理
  • 後期處理

多箇中間件處理,就造成了所謂的洋蔥模型,它是 AOP 面向切面編程的一種應用。

結合一下上面的果盤例子可知,在 Koa.js 中,蘋果(源數據)就是 請求數據 request,果盤(最終數據)就是 響應數據 response,中間處理的過程就是 Koa2.js 的中間件函數處理的過程

一張經典的洋蔥切面圖以下:

先回顧一下,Koa2.js 中下面代碼打印輸出順序爲:

const Koa = require('koa')
const app = new Koa()

app.use(async (cxt, next) => {
  console.log('middleware_01 start')
  await next()
  console.log('middleware_01 end')
})

app.use(async (cxt, next) => {
  console.log('middleware_02 start')
  await next()
  console.log('middleware_02 end')
})

app.use(async (cxt, next) => {
  console.log('middleware_03 start')
  console.log('middleware_03 end')
})

app.listen(3000)
// 瀏覽器訪問:http://localhost:3000
// 輸出順序爲:

middleware_01 start
middleware_02 start
middleware_03 start
middleware_03 end
middleware_02 end
middleware_01 end

如何實現洋蔥模型(中間件機制)

想想,怎樣才能實現 Koa.js 中間件處理機制呢?

最簡單版,以下代碼:

// 函數處理的數據
let context = {}

function middleware_01 (cxt) {
  console.log('middleware_01 start')
  middleware_02(cxt)
  console.log('middleware_01 end')
}

function middleware_02 (cxt) {
  console.log('middleware_02 start')
  middleware_03(cxt)
  console.log('middleware_02 end')
}

function middleware_03 (cxt) {
  console.log('middleware_03 start')
  console.log('middleware_03 end')
}

// 調用中間件 compose 函數
function compose () {
  // 默認調用第一個中間件
  middleware_01(context)
}

compose()

// 輸出結果以下,與上面中間件一致:

middleware_01 start
middleware_02 start
middleware_03 start
middleware_03 end
middleware_02 end
middleware_01 end

上面代碼雖然實現了,可是有不足點,如:

  • 要顯示指明要調用的函數名稱,不夠靈活

升級版:

const App = function () {
  // 中間件公共的處理數據
  let context = {}
  // 中間件隊列
  let middlewares = []
  return {
    // 將中間件放入隊列中
    use (fn) {
      middlewares.push(fn)
    },
    // 調用中間件
    callback () {
      // 初始調用 middlewares 隊列中的第 1 箇中間件
      return dispatch(0)
      function dispatch (i) {
        // 獲取要執行的中間件函數
        let fn = middlewares[i]
        // 執行中間件函數,回調參數是:公共數據、調用下一個中間件函數
        // 返回一個 Promise 實例
        return Promise.resolve(
          fn(context, function next () { dispatch(i + 1) })
        )
      }
    },
  }
}

上面代碼,在不考慮特殊邊界狀況下,就完成了 Koa2.js 中簡易版中間件的封裝,讓咱們來測試一下

// 測試代碼

let app = App()

app.use(async (cxt, next) => {
  console.log('middleware_01 start')
  await next()
  console.log('middleware_01 end')
})

app.use(async (cxt, next) => {
  console.log('middleware_02 start')
  await next()
  console.log('middleware_02 end')
})

app.use(async (cxt, next) => {
  console.log('middleware_03 start')
  console.log('middleware_03 end')
})

// Koa2.js 源碼中,放在 http.createServer(callback) 回調中調用
// 這裏咱們直接調用
app.callback()

// 輸出以下:

middleware_01 start
middleware_02 start
middleware_03 start
middleware_03 end
middleware_02 end
middleware_01 end

想更深刻的瞭解 Koa2.js 洋蔥模型可在這裏看源碼


歡迎關注無廣告文章公衆號:學前端

參考

<!-- 引用 -->

相關文章
相關標籤/搜索