摘要:本文來(lái)自心譚博客深入源碼架構(gòu)設(shè)計(jì)前端面試設(shè)計(jì)模式手冊(cè)教程實(shí)戰(zhàn)等更多專(zhuān)題,請(qǐng)來(lái)導(dǎo)航頁(yè)領(lǐng)取食用所有系列文章都放在了。歡迎交流和最近讀了的源碼,理清楚了架構(gòu)設(shè)計(jì)與用到的第三方庫(kù)。
本文來(lái)自《心譚博客·深入koa源碼:架構(gòu)設(shè)計(jì)》
前端面試、設(shè)計(jì)模式手冊(cè)、Webpack4教程、NodeJs實(shí)戰(zhàn)等更多專(zhuān)題,請(qǐng)來(lái)導(dǎo)航頁(yè)領(lǐng)取食用
所有系列文章都放在了Github。歡迎交流和Star ?? ヽ(°▽°)ノ ?
最近讀了 koa 的源碼,理清楚了架構(gòu)設(shè)計(jì)與用到的第三方庫(kù)。本系列將分為 3 篇,分別介紹 koa 的架構(gòu)設(shè)計(jì)和 3 個(gè)核心庫(kù)的原理,最終會(huì)手動(dòng)實(shí)現(xiàn)一個(gè)簡(jiǎn)易的 koa。
koa 的實(shí)現(xiàn)都在倉(cāng)庫(kù)的lib目錄下,如下圖所示,只有 4 個(gè)文件:
對(duì)于這四個(gè)文件,根據(jù)用途和封裝邏輯,可以分為 3 類(lèi):req 和 res,上下文以及 application。
req 和 res對(duì)應(yīng)的文件是:request.js 和 response.js。分別代表著客戶端請(qǐng)求信息和服務(wù)端返回信息。
這兩個(gè)文件在實(shí)現(xiàn)邏輯上完全一致。對(duì)外暴露都是一個(gè)對(duì)象,對(duì)象上的屬性都使用了getter或setter來(lái)實(shí)現(xiàn)讀寫(xiě)控制。
上下文對(duì)應(yīng)的文件是:context.js。存了運(yùn)行環(huán)境的上下文信息,例如cookies。
除此之外,因?yàn)?b>request和response都屬于上下文信息,所以通過(guò)delegate.js庫(kù)來(lái)實(shí)現(xiàn)了對(duì)request.js和response.js上所有屬性的代理。例如以下代碼:
/** * Response delegation. */ delegate(proto, "response") .method("attachment") .method("redirect"); /** * Request delegation. */ delegate(proto, "request") .method("acceptsLanguages") .method("acceptsEncodings");
使用代理的另外一個(gè)好處就是:更方便的訪問(wèn) req 和 res 上的屬性。比如在開(kāi)發(fā) koa 應(yīng)用的時(shí)候,可以通過(guò)ctx.headers來(lái)讀取客戶端請(qǐng)求的頭部信息,不需要寫(xiě)成ctx.res.headers了(這樣寫(xiě)沒(méi)錯(cuò))。
注意:req 和 res 并不是在context.js中被綁定到上下文的,而是在application被綁定到上下文變量ctx中的。原因是因?yàn)槊總€(gè)請(qǐng)求的 req/res 都不是相同的。
Application對(duì)應(yīng)的文件是: application.js。這個(gè)文件的邏輯是最重要的,它的作用主要是:
給用戶暴露服務(wù)啟動(dòng)接口
針對(duì)每個(gè)請(qǐng)求,生成新的上下文
處理中間件,將其串聯(lián)
對(duì)外暴露接口使用 koa 時(shí)候,我們常通過(guò)listen或者callback來(lái)啟動(dòng)服務(wù)器:
const app = new Koa(); app.listen(3000); // listen啟動(dòng) http.createServer(app.callback()).listen(3000); // callback啟動(dòng)
這兩種啟動(dòng)方法是完全等價(jià)的。因?yàn)?b>listen方法內(nèi)部,就調(diào)用了callback,并且將它傳給http.createServer。接著看一下callback這個(gè)方法主要做了什么:
調(diào)用koa-compose將中間件串聯(lián)起來(lái)(下文再講)。
生成傳給http.createServer()的函數(shù),并且返回。
http.createServer傳給函數(shù)參數(shù)的請(qǐng)求信息和返回信息,都被這個(gè)函數(shù)拿到了。并且傳給createContext方法,生成本次請(qǐng)求的上下文。
將生成的上下文傳給第 1 步生成的中間件調(diào)用鏈,這就是為什么我們?cè)谥虚g件處理邏輯的時(shí)候能夠訪問(wèn)ctx
生成新的上下文這里上下文的方法對(duì)應(yīng)的是createContext方法。這里我覺(jué)得更像語(yǔ)法糖,是為了讓 koa 使用者使用更方便。比如以下這段代碼:
// this.request 是 request.js 暴露出來(lái)的對(duì)象,將其引用保存在context.request中 // 用戶可以直接通過(guò) ctx.屬性名 來(lái)訪問(wèn)對(duì)應(yīng)屬性 const request = (context.request = Object.create(this.request)); // 這個(gè)req是本次請(qǐng)求信息,是由 http.createServer 傳遞給回調(diào)函數(shù)的 context.req = request.req = response.req = req;
讀到這里,雖然可以解釋 ctx.headers 是 ctx.request.headers 的語(yǔ)法糖這類(lèi)問(wèn)題。但是感覺(jué)怪怪的。就以這個(gè)例子,ctx.headers 訪問(wèn)的是 ctx.reqeust 上的 headers,而不是本次請(qǐng)求信息上的headers。本次請(qǐng)求信息掛在了ctx.req上。
讓我們?cè)倩氐?b>reqeust.js的源碼,看到了headers的 getter 實(shí)現(xiàn):
get headers() { return this.req.headers; }
ok,看來(lái)這里的this就是指的上下文環(huán)境咯。那么肯定是在application.js中某個(gè)地方改變了this的指向。果然,在application.js的構(gòu)造函數(shù)中可以看到:
this.request = Object.create(request);
application 實(shí)例上的 request 被傳遞給了 context.request,此時(shí) this 自然指向了 context。
可以看到,koa 為了讓開(kāi)發(fā)者使用方便,在上下文上做了很多工作。
中間件機(jī)制中間件的設(shè)計(jì)是 koa 最重要的部分,實(shí)現(xiàn)上用到了koa-compose庫(kù)來(lái)串聯(lián)中間件,形成“洋蔥模型”。關(guān)于這個(gè)庫(kù),放在第二篇關(guān)于 koa 核心庫(kù)的介紹中說(shuō)明。
application 中處理中間件的函數(shù)是use和handleRequest:
use函數(shù):傳入async/await函數(shù),并將其放入 application 實(shí)例上的middleware數(shù)組中。如果傳入是 generator,會(huì)調(diào)用koa-conver庫(kù)將其轉(zhuǎn)化為async/await函數(shù)。
handleRequest(ctx, fnMiddleware)函數(shù):傳入的fnMiddleware是已經(jīng)串聯(lián)好的中間件,函數(shù)所做的工作就是再其后再添加一個(gè)返回給客戶端的函數(shù)和錯(cuò)誤處理函數(shù)。返回給客戶端的函數(shù)其實(shí)就是respond函數(shù),里面通過(guò)調(diào)用res.end()來(lái)向客戶端返回信息,整個(gè)流程就走完了。
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://systransis.cn/yun/104916.html
摘要:最近讀了的源碼,理清楚了架構(gòu)設(shè)計(jì)與用到的第三方庫(kù)。本系列將分為篇,分別介紹的架構(gòu)設(shè)計(jì)和個(gè)核心庫(kù),最終會(huì)手動(dòng)實(shí)現(xiàn)一個(gè)簡(jiǎn)易的。本文來(lái)自心譚博客深入源碼核心庫(kù)原理所有系列文章都放在了。這一段邏輯封裝在了核心庫(kù)里面。 最近讀了 koa2 的源碼,理清楚了架構(gòu)設(shè)計(jì)與用到的第三方庫(kù)。本系列將分為 3 篇,分別介紹 koa 的架構(gòu)設(shè)計(jì)和 3 個(gè)核心庫(kù),最終會(huì)手動(dòng)實(shí)現(xiàn)一個(gè)簡(jiǎn)易的 koa。這是系列第 2...
摘要:閱讀好的框架的源碼有很多好處,從大神的視角去理解整個(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)某類(lèi)功能使用到的數(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)某類(lèi)功能使用到的數(shù)據(jù)結(jié)構(gòu)和算法等等。 使用koa 其實(shí)某個(gè)框架閱讀源碼的時(shí)候,首...
摘要:感謝大神的免費(fèi)的計(jì)算機(jī)編程類(lèi)中文書(shū)籍收錄并推薦地址,以后在倉(cāng)庫(kù)里更新地址,聲音版全文狼叔如何正確的學(xué)習(xí)簡(jiǎn)介現(xiàn)在,越來(lái)越多的科技公司和開(kāi)發(fā)者開(kāi)始使用開(kāi)發(fā)各種應(yīng)用。 說(shuō)明 2017-12-14 我發(fā)了一篇文章《沒(méi)用過(guò)Node.js,就別瞎逼逼》是因?yàn)橛腥嗽谥跎虾贜ode.js。那篇文章的反響還是相當(dāng)不錯(cuò)的,甚至連著名的hax賀老都很認(rèn)同,下班時(shí)讀那篇文章,竟然坐車(chē)的還坐過(guò)站了。大家可以很...
摘要:感謝大神的免費(fèi)的計(jì)算機(jī)編程類(lèi)中文書(shū)籍收錄并推薦地址,以后在倉(cāng)庫(kù)里更新地址,聲音版全文狼叔如何正確的學(xué)習(xí)簡(jiǎn)介現(xiàn)在,越來(lái)越多的科技公司和開(kāi)發(fā)者開(kāi)始使用開(kāi)發(fā)各種應(yīng)用。 說(shuō)明 2017-12-14 我發(fā)了一篇文章《沒(méi)用過(guò)Node.js,就別瞎逼逼》是因?yàn)橛腥嗽谥跎虾贜ode.js。那篇文章的反響還是相當(dāng)不錯(cuò)的,甚至連著名的hax賀老都很認(rèn)同,下班時(shí)讀那篇文章,竟然坐車(chē)的還坐過(guò)站了。大家可以很...
閱讀 3560·2021-09-06 15:13
閱讀 1543·2021-09-02 10:19
閱讀 2491·2019-08-30 15:52
閱讀 932·2019-08-29 15:25
閱讀 1580·2019-08-26 18:36
閱讀 511·2019-08-26 13:23
閱讀 1349·2019-08-26 10:46
閱讀 3515·2019-08-26 10:41