摘要:前言被認(rèn)為是第二代,它最大的特點(diǎn)就是獨(dú)特的中間件流程控制,是一個(gè)典型的洋蔥模型。這段代碼就很巧妙的實(shí)現(xiàn)了兩點(diǎn)將一路傳下去給中間件將中的下一個(gè)中間件作為未來的返回值這兩點(diǎn)也是洋蔥模型實(shí)現(xiàn)的核心。
前言
koa被認(rèn)為是第二代node web framework,它最大的特點(diǎn)就是獨(dú)特的中間件流程控制,是一個(gè)典型的洋蔥模型。koa和koa2中間件的思路是一樣的,但是實(shí)現(xiàn)方式有所區(qū)別,koa2在node7.6之后更是可以直接用async/await來替代generator使用中間件,本文以最后一種情況舉例。
洋蔥模型下面兩張圖是網(wǎng)上找的,很清晰的表明了一個(gè)請(qǐng)求是如何經(jīng)過中間件最后生成響應(yīng)的,這種模式中開發(fā)和使用中間件都是非常方便的
來看一個(gè)koa2的demo:
const Koa = require("koa"); const app = new Koa(); const PORT = 3000; // #1 app.use(async (ctx, next)=>{ console.log(1) await next(); console.log(1) }); // #2 app.use(async (ctx, next) => { console.log(2) await next(); console.log(2) }) app.use(async (ctx, next) => { console.log(3) }) app.listen(PORT); console.log(`http://localhost:${PORT}`);
訪問http://localhost:3000,控制臺(tái)打?。?/p>
1 2 3 2 1
怎么樣,是不是有一點(diǎn)點(diǎn)感覺了。當(dāng)程序運(yùn)行到await next()的時(shí)候就會(huì)暫停當(dāng)前程序,進(jìn)入下一個(gè)中間件,處理完之后才會(huì)仔回過頭來繼續(xù)處理。也就是說,當(dāng)一個(gè)請(qǐng)求進(jìn)入,#1會(huì)被第一個(gè)和最后一個(gè)經(jīng)過,#2則是被第二和倒數(shù)第二個(gè)經(jīng)過,依次類推。
實(shí)現(xiàn)koa的實(shí)現(xiàn)有幾個(gè)最重要的點(diǎn)
context的保存和傳遞
中間件的管理和next的實(shí)現(xiàn)
翻看源碼我們發(fā)現(xiàn)
app.listen使用了this.callback()來生成node的httpServer的回調(diào)函數(shù)
listen(...args) { debug("listen"); const server = http.createServer(this.callback()); return server.listen(...args); }
那就再來看this. callback
callback() { const fn = compose(this.middleware); if (!this.listeners("error").length) this.on("error", this.onerror); const handleRequest = (req, res) => { const ctx = this.createContext(req, res); return this.handleRequest(ctx, fn); }; return handleRequest; }
這里用compose處理了一下this.middleware,創(chuàng)建了ctx并賦值為createContext的返回值,最后返回了handleRequest。
this.middleware看起來應(yīng)該是中間件的集合,查了下代碼,果不其然:
this.middleware = [];
use(fn) { if (typeof fn !== "function") throw new TypeError("middleware must be a function!"); if (isGeneratorFunction(fn)) { deprecate("Support for generators will be removed in v3. " + "See the documentation for examples of how to convert old middleware " + "https://github.com/koajs/koa/blob/master/docs/migration.md"); fn = convert(fn); } debug("use %s", fn._name || fn.name || "-"); this.middleware.push(fn); return this; }
拋開兼容和判斷,這段代碼只做了一件事:
use(fn) { this.middleware.push(fn); return this; }
原來當(dāng)我們app.use的時(shí)候,只是把方法存在了一個(gè)數(shù)組里。
那么compose 又是什么呢。跟蹤源碼可以看到compose來自koa-compose模塊,代碼也不多:(去掉了一些不影響主邏輯的判斷)
function compose (middleware) { return function (context, next) { // last called middleware # let index = -1 return dispatch(0) function dispatch (i) { if (i <= index) return Promise.reject(new Error("next() called multiple times")) index = i let fn = middleware[i] if (i === middleware.length) fn = next if (!fn) return Promise.resolve() try { return Promise.resolve(fn(context, function next () { return dispatch(i + 1) })) } catch (err) { return Promise.reject(err) } } } }
比較關(guān)鍵的就是這個(gè)dispatch函數(shù)了,它將遍歷整個(gè)middleware,然后將context和dispatch(i + 1)傳給middleware中的方法。
return Promise.resolve(fn(context, function next () { return dispatch(i + 1) }))
這段代碼就很巧妙的實(shí)現(xiàn)了兩點(diǎn):
1. 將`context`一路傳下去給中間件 2. 將`middleware`中的下一個(gè)中間件`fn`作為未來`next`的返回值
這兩點(diǎn)也是洋蔥模型實(shí)現(xiàn)的核心。
再往下看代碼實(shí)際上就沒有太多花樣了。
createContext和handleRequest 做的事實(shí)際上是把ctx和中間件進(jìn)行綁定,也就是第一次調(diào)用compose 返回值的地方。
createContext(req, res) { const context = Object.create(this.context); const request = context.request = Object.create(this.request); const response = context.response = Object.create(this.response); context.app = request.app = response.app = this; context.req = request.req = response.req = req; context.res = request.res = response.res = res; request.ctx = response.ctx = context; request.response = response; response.request = request; context.originalUrl = request.originalUrl = req.url; context.cookies = new Cookies(req, res, { keys: this.keys, secure: request.secure }); request.ip = request.ips[0] || req.socket.remoteAddress || ""; context.accept = request.accept = accepts(req); context.state = {}; return context; } 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); }
??
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://systransis.cn/yun/93712.html
摘要:任何一層報(bào)錯(cuò),都能用捕獲總結(jié)是一個(gè)非常輕量級(jí)的框架,只實(shí)現(xiàn)了中間件處理流程和對(duì)對(duì)象的封裝。其他的功能都由外部中間件提供。 koa 的中間件機(jī)制巧妙的運(yùn)用了閉包和 async await 的特點(diǎn),形成了一個(gè)洋蔥式的流程,和 JS 的事件流 (捕獲 -> target -> 冒泡) 相似 handleRequest(ctx, fnMiddleware) { const res ...
摘要:用法源碼由在年創(chuàng)建的科技術(shù)語。我們除去源碼校驗(yàn)函數(shù)部分,從最終返回的大的來看。這個(gè)返回值無法被識(shí)別。洋蔥模型我們來看源碼源碼每個(gè)都以作為參數(shù)進(jìn)行注入,返回一個(gè)新的鏈。改變?cè)冀M數(shù),是一種副作用。 @(Redux)[|用法|源碼] Redux 由Dan Abramov在2015年創(chuàng)建的科技術(shù)語。是受2014年Facebook的Flux架構(gòu)以及函數(shù)式編程語言Elm啟發(fā)。很快,Redux因其...
摘要:實(shí)現(xiàn)的四大模塊上文簡(jiǎn)述了源碼的大體框架結(jié)構(gòu),接下來我們來實(shí)現(xiàn)一個(gè)的框架,筆者認(rèn)為理解和實(shí)現(xiàn)一個(gè)框架需要實(shí)現(xiàn)四個(gè)大模塊,分別是封裝創(chuàng)建類構(gòu)造函數(shù)構(gòu)造對(duì)象中間件機(jī)制和剝洋蔥模型的實(shí)現(xiàn)錯(cuò)誤捕獲和錯(cuò)誤處理下面我們就逐一分析和實(shí)現(xiàn)。 什么是koa框架? ? ? ? ?koa是一個(gè)基于node實(shí)現(xiàn)的一個(gè)新的web框架,它是由express框架的原班人馬打造的。它的特點(diǎn)是優(yōu)雅、簡(jiǎn)潔、表達(dá)力強(qiáng)、自由度...
摘要:實(shí)現(xiàn)的四大模塊上文簡(jiǎn)述了源碼的大體框架結(jié)構(gòu),接下來我們來實(shí)現(xiàn)一個(gè)的框架,筆者認(rèn)為理解和實(shí)現(xiàn)一個(gè)框架需要實(shí)現(xiàn)四個(gè)大模塊,分別是封裝創(chuàng)建類構(gòu)造函數(shù)構(gòu)造對(duì)象中間件機(jī)制和剝洋蔥模型的實(shí)現(xiàn)錯(cuò)誤捕獲和錯(cuò)誤處理下面我們就逐一分析和實(shí)現(xiàn)。 什么是koa框架? ? ? ? ?koa是一個(gè)基于node實(shí)現(xiàn)的一個(gè)新的web框架,它是由express框架的原班人馬打造的。它的特點(diǎn)是優(yōu)雅、簡(jiǎn)潔、表達(dá)力強(qiáng)、自由度...
閱讀 3452·2021-10-14 09:42
閱讀 2740·2021-09-08 10:44
閱讀 1315·2021-09-02 10:18
閱讀 3639·2021-08-30 09:43
閱讀 2811·2021-07-29 13:49
閱讀 3731·2019-08-29 17:02
閱讀 1590·2019-08-29 15:09
閱讀 1043·2019-08-29 11:01