日本综合一区二区|亚洲中文天堂综合|日韩欧美自拍一区|男女精品天堂一区|欧美自拍第6页亚洲成人精品一区|亚洲黄色天堂一区二区成人|超碰91偷拍第一页|日韩av夜夜嗨中文字幕|久久蜜综合视频官网|精美人妻一区二区三区

RELATEED CONSULTING
相關(guān)咨詢
選擇下列產(chǎn)品馬上在線溝通
服務(wù)時(shí)間:8:30-17:00
你可能遇到了下面的問題
關(guān)閉右側(cè)工具欄

新聞中心

這里有您想知道的互聯(lián)網(wǎng)營銷解決方案
你需要掌握的 Koa 洋蔥模型和中間件

大家好,我是前端西瓜哥。

Koa 是一個(gè) nodejs 框架,經(jīng)常用于寫 web 后端服務(wù)。它是 Express 框架的原班人馬開發(fā)的新一代 web 框架,使用了 async / await 來優(yōu)雅處理無處不在的異步邏輯。

我們常說 Koa 其實(shí)是洋蔥模型,今天就來深挖下 Koa 的洋蔥模型到底是什么。

什么是洋蔥模型

在這之前,我們先簡單看看 Koa 是如何使用的。

在 Koa 中,我們通過 app.use 方法注冊中間件。中間件可以注冊多個(gè),它們的執(zhí)行順序和注冊時(shí)機(jī)相關(guān),先注冊的先執(zhí)行。

所謂中間件就是一個(gè)函數(shù),這個(gè)函數(shù)接受 Koa 提供的兩個(gè)參數(shù):

  1. ctx 上下文對象;
  2. next 函數(shù)。

ctx 上有各種參數(shù),比如請求對象 request 和響應(yīng)對象 response。

調(diào)用 next 函數(shù)會執(zhí)行下一個(gè)的中間件,如果你不調(diào)用 next 函數(shù),那下一個(gè)中間件就不會執(zhí)行。

我們看一個(gè)例子:

const Koa = require('koa');

const app = new Koa();

// 中間件 1:記錄請求花費(fèi)時(shí)間
app.use(async (ctx, next) => {
console.log('中間件 1');
const start = new Date().getTime();
await next();
const t = new Date().getTime() - start;
console.log('請求花費(fèi)時(shí)間為', t + ' ms');
})

// 中間件 2:獲取數(shù)據(jù)
app.use(async (ctx, next) => {
console.log('中間件 2')
const data = await getData();
ctx.body = { data };
})

// 模擬從數(shù)據(jù)庫獲取數(shù)據(jù),耗時(shí) 1 s
const getData = async () => {
await new Promise((resolve) => {
setTimeout(() {
resolve();
}, 1000);
})
return 'Hello World!';
}

app.listen(3005);

當(dāng)請求時(shí),服務(wù)器的日志是這樣的:

中間件 
中間件 2
請求花費(fèi)時(shí)間為 1005 ms

從中間件 1 睡覺來看來說,代碼邏輯可以分為三部分:

  1. 先執(zhí)行next() 。
  2. 然后執(zhí)行next() 其后的中間件 2 的所有代碼。
  3. 最后執(zhí)行next() 后面的代碼。

這種先執(zhí)行了當(dāng)前中間件的前半部分邏輯,然后處理完之后的中間件后,最后繼續(xù)執(zhí)行當(dāng)前中間件的后半部分的特性,可以讓我們可以像洋蔥一樣,從外到內(nèi)先處理 request 對象,再從內(nèi)到外處理 response 對象,于是被稱為 洋蔥模型。

洋蔥模式本質(zhì)是設(shè)計(jì)模式中的 職責(zé)鏈模式 的變體。

職責(zé)鏈模式,指的是將請求和響應(yīng)解耦,讓多個(gè)處理對象有機(jī)會依此去處理請求。比如處理對象 A 先處理數(shù)據(jù),然后將處理后的數(shù)據(jù)傳給處理對象 B,依此類推形成了一條鏈。鏈條上的不同處理對象負(fù)責(zé)各自的職責(zé)。

A -> B -> C

相比經(jīng)典的職責(zé)鏈模式,洋蔥模型可以將一個(gè)處理器分成兩個(gè)部分,在不同時(shí)機(jī)觸發(fā)但卻擁有相同的上下文,在一些情況下是非常好用的,就比如剛剛提到的打印單個(gè)請求花費(fèi)時(shí)長。

A1 -> B1 -> C -> B2 -> A2

Koa 中的源碼實(shí)現(xiàn)

Koa 是一個(gè)非常輕量的庫,源碼分析起來相對比較容易,所以我們來看看它的洋蔥模型,也就是中間件模型的實(shí)現(xiàn)吧。不過因?yàn)橛玫搅舜罅块]包的實(shí)現(xiàn),看起來還是容易眼花的。

首先通過 new Koa() 創(chuàng)建的一個(gè) app 對象,它有一個(gè)成員屬性 middleware,初始值為空數(shù)組。這個(gè) middleware 就是保存中間件函數(shù)的地方。

每當(dāng)我們調(diào)用 app.use(fn) 時(shí),Koa 會將中間件函數(shù)加到 middleware。

use (fn) {
this.middleware.push(fn)
return this
}

最后我們調(diào)用 app.listen(port),這個(gè) API 會啟動 http 服務(wù)器。

listen (...args) {
const server = http.createServer(this.callback())
return server.listen(...args)
}

this.callback 是一個(gè)函數(shù),會返回一個(gè)封裝好的函數(shù)給 nodejs 原生的 http.createServer 使用。

callback 實(shí)現(xiàn)為:

callback () {
const fn = this.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
}

再看看這個(gè) this.compose 方法,它將多個(gè)中間件函數(shù)進(jìn)行組合,讓它們可以依此被調(diào)用。這個(gè) compose 被抽成一個(gè)名為 koa-compose 的 npm 包,里面的代碼很少,我將其中的核心代碼抽出來:

function compose (middleware) {
return function (context, next) {
function dispatch (i) {
let fn = middleware[i]
if (i === middleware.length) fn = next
// 走到最后一個(gè)中間件
if (!fn) return Promise.resolve()
return Promise.resolve(fn(context, dispatch.bind(null, i + 1)))
}
return dispatch(0)
}
}

compose 函數(shù)的作用是將中間件函數(shù)進(jìn)行組裝,先返回第一個(gè)中間件函數(shù)的封裝,其類型簽名為 () => Promise。

當(dāng)這個(gè)被封裝的函數(shù)被執(zhí)行時(shí),它會將執(zhí)行原始的中間件函數(shù),并拿到下一個(gè)中間件函數(shù)的封裝,也就是 next。

我們回到 callback 方法中,其中 this.handleRequest 的實(shí)現(xiàn)為:

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)
}

this.handleRequest 將 compose 返回的第一個(gè)中間件,進(jìn)行調(diào)用。

Express 是洋蔥模型嗎?

Express 發(fā)布時(shí),ES6 還沒出來,不能使用 Promise,更不用說 ES7 的 async / await 了。

所以 Express 在當(dāng)時(shí)情況下,其實(shí)并沒有能力實(shí)現(xiàn)這種支持異步的洋蔥模型,那時(shí)候要做異步就只能使用回調(diào)的風(fēng)格。

總的來說,Express 也算是一種 只支持同步的洋蔥模型,因?yàn)樗趯?shí)現(xiàn)上沒有處理 next 是 async 的情況,這是歷史原因?qū)е碌摹?/p>

Express 是在調(diào)用 res.send  時(shí),結(jié)束數(shù)據(jù)的處理,返回響應(yīng)數(shù)據(jù)給客戶的。在一個(gè)請求里不能多次調(diào)用 res.rend。

Koa 是給 ctx.response 上加內(nèi)容,等到中間件走完才返回?cái)?shù)據(jù)。

結(jié)尾

洋蔥模型,就是將數(shù)據(jù)順序傳入到多個(gè)中間件中,讓它們進(jìn)行處理傳遞,并利用函數(shù)遞歸的特性,讓我們可以在一個(gè)中間件內(nèi)先執(zhí)行前半部分邏輯,再執(zhí)行之后的所有中間件的完整邏輯后,再掉轉(zhuǎn)方向繼續(xù)執(zhí)行這個(gè)中間件的后半部分。

相比一旦進(jìn)入下個(gè)中間件后再不回來,這種實(shí)現(xiàn)可以讓我們的代碼更靈活,在一些場景下很有用。


當(dāng)前標(biāo)題:你需要掌握的 Koa 洋蔥模型和中間件
瀏覽路徑:http://www.dlmjj.cn/article/djcgeso.html