摘要:設(shè)置了一個(gè)出事索引值調(diào)用函數(shù),開始時(shí)候傳進(jìn)去聲明函數(shù),把傳進(jìn)來的賦值給拿出第個(gè)中間件函數(shù),賦值給判斷如果等于的長度就把賦值給如果是假的返回一個(gè)同時(shí)執(zhí)行,也就是中間件,把函數(shù)傳遞到里面遞歸調(diào)用自己上面的代碼是這個(gè)部分的精華。
koa的中間件執(zhí)行的流程控制,代碼的是非常精妙的。由下面的一張洋蔥模型的圖來形容,記住這張圖。
為什么是這樣子的圖,下面我們有一個(gè)例子來描述一下
const Koa = require("koa") const app = new Koa() // fn1 app.use(async (ctx, next) => { console.log("fn1-1") next() console.log("fn1-2") }) // fn2 app.use(async (ctx, next) => { console.log("fn2-1") next() console.log("fn2-2") }) // fn3 app.use(async (ctx, next) => { console.log("fn3-1") next() console.log("fn3-2") }) app.listen(4002) // fn1-1、fn2-1、fn3-1、fn3-2、fn2-2、fn1-2
上面的這個(gè)例子,順序打印出來的是fn1-1、fn2-1、fn3-1、fn3-2、fn2-2、fn1-2,現(xiàn)在只知道,調(diào)用next()函數(shù)就會(huì)把控制流程就跳到下一個(gè)中間件,知道執(zhí)行所有完之后然后再逐步向上執(zhí)行前一個(gè)next后面的代碼。這根跟洋蔥有很大的相像似性(如果你愿意一層一層一層的剝開我的心~~~)。
探索但是其中的原理是什么呢??下面我們一步步去探索。
首先是調(diào)用 app.use(fn) 這行代碼,這行代碼在源碼里面,刪除一些代碼判斷,是這樣子的
constructor() { super(); this.middleware = []; } use(fn) { this.middleware.push(fn); return this; }
就是把所有函數(shù)push到一個(gè)middleware的數(shù)組之中,這個(gè)use就是專門干這中勾當(dāng)?shù)摹?/p>
好了知道use的作用了,執(zhí)行了use之后 我們的middleware中就有很多中間件函數(shù)了,下面我們繼續(xù)看下去。
然后執(zhí)行到 app.listen函數(shù)之后,代碼如下
listen(...args) { // 創(chuàng)建一個(gè)server const server = http.createServer(this.callback()); return server.listen(...args); }
我們看到里么有個(gè)this.callback()執(zhí)行函數(shù),然后我們跳到這個(gè)函數(shù)里面。
callback() { // 我們看這里 const fn = compose(this.middleware); const handleRequest = (req, res) => { const ctx = this.createContext(req, res); // 這個(gè)節(jié)點(diǎn)我們請(qǐng)記住下面這一行代碼 return this.handleRequest(ctx, fn); }; return handleRequest; }
這個(gè)callback函數(shù)里面,執(zhí)行了compose函數(shù),并且把middleware數(shù)組作為參數(shù)傳遞進(jìn)去。
執(zhí)行到了compose函數(shù),下面我們就看看compose里面有什么。
compose函數(shù)就是一開始引用了koa-compose模塊,簡化之后發(fā)現(xiàn)里面的代碼如下,簡化后就簡簡單單的20幾行代碼,后面會(huì)詳細(xì)解釋下面的代碼。
function compose (middleware) { return function (context, next) { let index = -1 return dispatch(0) function dispatch (i) { 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) } } } }
執(zhí)行這個(gè)compose返回一個(gè)函數(shù),這也是最核心的一個(gè)函數(shù)。注意這是上面的callback調(diào)用的。得到一個(gè)fn函數(shù)
看上面的callback調(diào)用的
然后執(zhí)行到this.handleRequest(ctx, fn); 這個(gè)函數(shù)吧ctx和fn(這個(gè)就是上面compose返回的函數(shù))作為參數(shù),傳入到this.handleRequest中。 代碼如下。
handleRequest(ctx, fnMiddleware) { return fnMiddleware(ctx).then(handleResponse).catch(onerror); }
到這里才真正的執(zhí)行了compose返回的函數(shù),把ctx傳進(jìn)去。然后我們繼續(xù)看這個(gè)函數(shù)fnMiddleware(ctx),其實(shí)就是下面這樣子的。
function (context, next) { // 設(shè)置了一個(gè)出事索引值 let index = -1 // 調(diào)用dispatch函數(shù),開始時(shí)候傳0進(jìn)去 return dispatch(0) // 聲明dispatch函數(shù), function dispatch (i) { // 把傳進(jìn)來的賦值給index index = i // 拿出middleware第i個(gè)中間件函數(shù),賦值給fn let fn = middleware[i] // 判斷如果i 等于middleware的長度 就把next 賦值給 fn if (i === middleware.length) fn = next // 如果fn是假的 return return Promise.resolve() if (!fn) return Promise.resolve() try { // 返回一個(gè)Promise.resolve, 同時(shí)執(zhí)行fn, 也就是中間件,把next 函數(shù)傳遞到fn里面 return Promise.resolve(fn(context, function next () { // 遞歸調(diào)用自己 return dispatch(i + 1) })) } catch (err) { return Promise.reject(err) } } }
上面的代碼是這個(gè)部分的精華。這里詳細(xì)的說一下,首先定義了一個(gè)index和dispatch函數(shù), 然后一開始調(diào)用dispatch(0)函數(shù),里面把0賦值給了index,然后從middleware的數(shù)組(例子中我們有三個(gè)中間件函數(shù))中拿到第0個(gè)中間件函數(shù),賦值給fn,經(jīng)過兩個(gè)if都不符合條件,然后執(zhí)行
return Promise.resolve(fn(context, function next () { // 遞歸調(diào)用自己 return dispatch(i + 1) }))
這里的執(zhí)行fn 中間件函數(shù),并且把ctx 和 function next () { return dispatch(i + 1) }) 作為參數(shù)傳遞進(jìn)去。這個(gè)時(shí)候代碼如下一幕了然
app.use(async (ctx, next) => { console.log("fn1-1") next() // 執(zhí)行傳入的next console.log("fn1-2") })
執(zhí)行這個(gè)函數(shù) 就會(huì)打印出fn1-1 然后就會(huì)執(zhí)行next()函數(shù),看上上一塊代碼,執(zhí)行next()函數(shù)里面會(huì)調(diào)用 dispatch(i + 1) 也就是調(diào)用第fn = middleware[1] 正是第二個(gè)中間件。
看到這里大家就大概明白了。然后進(jìn)入第二個(gè)中間件執(zhí)行fn,打印出fn2-1,繼續(xù)執(zhí)行next()函數(shù),next函數(shù)里面繼續(xù)調(diào)用 dispatch(i + 1) ,
也就是fn = middleware[2] 第三中間件函數(shù),打印出fn3-1,繼續(xù)執(zhí)行next()函數(shù)里面會(huì)調(diào)用 dispatch(i + 1),也就是fn = middleware[3] ,
這里注意了,if (i === middleware.length) fn = next到這里會(huì)符合這個(gè)條件,然后把next 賦值給fn 這里的next就是這個(gè)fnMiddleware(ctx).then(handleResponse).catch(onerror);調(diào)用時(shí)候傳入的,然而這里并沒有傳入,所以這時(shí)候 fn 就是 undefined,然后繼續(xù)執(zhí)行到if (!fn) return Promise.resolve() 返回一個(gè)空的值,這就是第三個(gè)中間件的next執(zhí)行結(jié)果,
然后繼續(xù)執(zhí)行下一行就打印出了fn3-2,最后向上執(zhí)行到fn2-2,然后到fn1-2, 整個(gè)中間件的執(zhí)行過程。很像洋蔥模型,一層層進(jìn)入,然后一層層出來。
好了整個(gè)中間件執(zhí)行過程就是醬紫啦~~~
最后安利一波博客: https://github.com/naihe138/n...
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://m.hztianpu.com/yun/95475.html
摘要:閱讀好的框架的源碼有很多好處,從大神的視角去理解整個(gè)框架的設(shè)計(jì)思想。使用其實(shí)某個(gè)框架閱讀源碼的時(shí)候,首先我們要會(huì)去用這個(gè)框架,因?yàn)橛昧宋覀儾胖溃硞€(gè)是怎么用,哪里有坑,哪里設(shè)計(jì)的精妙。 閱讀好的框架的源碼有很多好處,從大神的視角去理解整個(gè)框架的設(shè)計(jì)思想。大到架構(gòu)設(shè)計(jì),小到可取的命名風(fēng)格,還有設(shè)計(jì)模式、實(shí)現(xiàn)某類功能使用到的數(shù)據(jù)結(jié)構(gòu)和算法等等。 使用koa 其實(shí)某個(gè)框架閱讀源碼的時(shí)候,首...
摘要:閱讀好的框架的源碼有很多好處,從大神的視角去理解整個(gè)框架的設(shè)計(jì)思想。使用其實(shí)某個(gè)框架閱讀源碼的時(shí)候,首先我們要會(huì)去用這個(gè)框架,因?yàn)橛昧宋覀儾胖?,某個(gè)是怎么用,哪里有坑,哪里設(shè)計(jì)的精妙。 閱讀好的框架的源碼有很多好處,從大神的視角去理解整個(gè)框架的設(shè)計(jì)思想。大到架構(gòu)設(shè)計(jì),小到可取的命名風(fēng)格,還有設(shè)計(jì)模式、實(shí)現(xiàn)某類功能使用到的數(shù)據(jù)結(jié)構(gòu)和算法等等。 使用koa 其實(shí)某個(gè)框架閱讀源碼的時(shí)候,首...
摘要:最近一年零零散散看了不少開源項(xiàng)目的源碼多少也有點(diǎn)心得這里想通過這篇文章總結(jié)一下這里以為例前段時(shí)間其實(shí)看過的源碼但是發(fā)現(xiàn)理解的有點(diǎn)偏差所以重新過一遍不得不說閱讀的代碼真的收獲很大沒啥奇技淫巧代碼優(yōu)雅設(shè)計(jì)極好注釋什么的就更不用說了總之還是推薦把 最近一年零零散散看了不少開源項(xiàng)目的源碼, 多少也有點(diǎn)心得, 這里想通過這篇文章總結(jié)一下, 這里以Koa為例, 前段時(shí)間其實(shí)看過Koa的源碼, 但是...
閱讀 1322·2023-04-25 18:57
閱讀 2231·2023-04-25 16:28
閱讀 4052·2021-11-24 09:39
閱讀 3708·2021-11-16 11:45
閱讀 1943·2021-10-13 09:40
閱讀 1313·2019-08-30 15:52
閱讀 1788·2019-08-30 10:57
閱讀 720·2019-08-29 16:55