摘要:實(shí)現(xiàn)的四大模塊上文簡(jiǎn)述了源碼的大體框架結(jié)構(gòu),接下來(lái)我們來(lái)實(shí)現(xiàn)一個(gè)的框架,筆者認(rèn)為理解和實(shí)現(xiàn)一個(gè)框架需要實(shí)現(xiàn)四個(gè)大模塊,分別是封裝創(chuàng)建類(lèi)構(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)、自由度高。它更express相比,它是一個(gè)更輕量的node框架,因?yàn)樗泄δ芏纪ㄟ^(guò)插件實(shí)現(xiàn),這種插拔式的架構(gòu)設(shè)計(jì)模式,很符合unix哲學(xué)。
? ? ? ?koa框架現(xiàn)在更新到了2.x版本,本文從零開(kāi)始,循序漸進(jìn),講解koa2的框架源碼結(jié)構(gòu)和實(shí)現(xiàn)原理,展示和詳解koa2框架源碼中的幾個(gè)最重要的概念,然后手把手教大家親自實(shí)現(xiàn)一個(gè)簡(jiǎn)易的koa2框架,幫助大家學(xué)習(xí)和更深層次的理解koa2,看完本文以后,再去對(duì)照koa2的源碼進(jìn)行查看,相信你的思路將會(huì)非常的順暢。
? ? ? ?本文所用的框架是koa2,它跟koa1不同,koa1使用的是generator+co.js的執(zhí)行方式,而koa2中使用了async/await,因此本文的代碼和demo需要運(yùn)行在node 8版本及其以上,如果讀者的node版本較低,建議升級(jí)或者安裝babel-cli,用其中的babel-node來(lái)運(yùn)行本文涉及到的代碼。
? ? ? ?本文實(shí)現(xiàn)的輕量版koa的完整代碼gitlab地址為:article_koa2
koa源碼結(jié)構(gòu)? ? ? ?上圖是koa2的源碼目錄結(jié)構(gòu)的lib文件夾,lib文件夾下放著四個(gè)koa2的核心文件:application.js、context.js、request.js、response.js。
application.js? ? ? ? application.js是koa的入口文件,它向外導(dǎo)出了創(chuàng)建class實(shí)例的構(gòu)造函數(shù),它繼承了events,這樣就會(huì)賦予框架事件監(jiān)聽(tīng)和事件觸發(fā)的能力。application還暴露了一些常用的api,比如toJSON、listen、use等等。
? ? ? ? listen的實(shí)現(xiàn)原理其實(shí)就是對(duì)http.createServer進(jìn)行了一個(gè)封裝,重點(diǎn)是這個(gè)函數(shù)中傳入的callback,它里面包含了中間件的合并,上下文的處理,對(duì)res的特殊處理。
? ? ? ? use是收集中間件,將多個(gè)中間件放入一個(gè)緩存隊(duì)列中,然后通過(guò)koa-compose這個(gè)插件進(jìn)行遞歸組合調(diào)用這一些列的中間件。
context.js? ? ? ? 這部分就是koa的應(yīng)用上下文ctx,其實(shí)就一個(gè)簡(jiǎn)單的對(duì)象暴露,里面的重點(diǎn)在delegate,這個(gè)就是代理,這個(gè)就是為了開(kāi)發(fā)者方便而設(shè)計(jì)的,比如我們要訪問(wèn)ctx.repsponse.status但是我們通過(guò)delegate,可以直接訪問(wèn)ctx.status訪問(wèn)到它。
request.js、response.js? ? ? ? 這兩部分就是對(duì)原生的res、req的一些操作了,大量使用es6的get和set的一些語(yǔ)法,去取headers或者設(shè)置headers、還有設(shè)置body等等,這些就不詳細(xì)介紹了,有興趣的讀者可以自行看源碼。
實(shí)現(xiàn)koa2的四大模塊? ? ? ? 上文簡(jiǎn)述了koa2源碼的大體框架結(jié)構(gòu),接下來(lái)我們來(lái)實(shí)現(xiàn)一個(gè)koa2的框架,筆者認(rèn)為理解和實(shí)現(xiàn)一個(gè)koa框架需要實(shí)現(xiàn)四個(gè)大模塊,分別是:
封裝node http server、創(chuàng)建Koa類(lèi)構(gòu)造函數(shù)
構(gòu)造request、response、context對(duì)象
中間件機(jī)制和剝洋蔥模型的實(shí)現(xiàn)
錯(cuò)誤捕獲和錯(cuò)誤處理
下面我們就逐一分析和實(shí)現(xiàn)。
模塊一:封裝node http server和創(chuàng)建Koa類(lèi)構(gòu)造函數(shù)? ? ? ? 閱讀koa2的源碼得知,實(shí)現(xiàn)koa的服務(wù)器應(yīng)用和端口監(jiān)聽(tīng),其實(shí)就是基于node的原生代碼進(jìn)行了封裝,如下圖的代碼就是通過(guò)node原生代碼實(shí)現(xiàn)的服務(wù)器監(jiān)聽(tīng)。
let http = require("http"); let server = http.createServer((req, res) => { res.writeHead(200); res.end("hello world"); }); server.listen(3000, () => { console.log("listenning on 3000"); });
? ? ? ? 我們需要將上面的node原生代碼封裝實(shí)現(xiàn)成koa的模式:
const http = require("http"); const Koa = require("koa"); const app = new Koa(); app.listen(3000);
? ? ? ? 實(shí)現(xiàn)koa的第一步就是對(duì)以上的這個(gè)過(guò)程進(jìn)行封裝,為此我們需要?jiǎng)?chuàng)建application.js實(shí)現(xiàn)一個(gè)Application類(lèi)的構(gòu)造函數(shù)
let http = require("http"); class Application { constructor() { this.callbackFunc; } listen(port) { let server = http.createServer(this.callback()); server.listen(port); } use(fn) { this.callbackFunc = fn; } callback() { return (req, res) => { this.callbackFunc(req, res); }; } } module.exports = Application;
? ? ? ? 然后創(chuàng)建example.js,引入application.js,運(yùn)行服務(wù)器實(shí)例啟動(dòng)監(jiān)聽(tīng)代碼:
let Koa = require("./application"); let app = new Koa(); app.use((req, res) => { res.writeHead(200); res.end("hello world"); }); app.listen(3000, () => { console.log("listening on 3000"); });
? ? ? ? 現(xiàn)在在瀏覽器輸入localhost:3000即可看到瀏覽器里顯示“hello world”?,F(xiàn)在第一步我們已經(jīng)完成了,對(duì)http server進(jìn)行了簡(jiǎn)單的封裝和創(chuàng)建了一個(gè)可以生成koa實(shí)例的類(lèi)class,這個(gè)類(lèi)里還實(shí)現(xiàn)了app.use用來(lái)注冊(cè)中間件和注冊(cè)回調(diào)函數(shù),app.listen用來(lái)開(kāi)啟服務(wù)器實(shí)例并傳入callback回調(diào)函數(shù),第一模塊主要是實(shí)現(xiàn)典型的koa風(fēng)格和搭好了一個(gè)koa的簡(jiǎn)單的架子。接下來(lái)我們開(kāi)始編寫(xiě)和講解第二模塊。
模塊二:構(gòu)造request、response、context對(duì)象? ? ? ? 閱讀koa2的源碼得知,其中context.js、request.js、response.js三個(gè)文件分別是request、response、context三個(gè)模塊的代碼文件。context就是我們平時(shí)寫(xiě)koa代碼時(shí)的ctx,它相當(dāng)于一個(gè)全局的koa實(shí)例上下文this,它連接了request、response兩個(gè)功能模塊,并且暴露給koa的實(shí)例和中間件等回調(diào)函數(shù)的參數(shù)中,起到承上啟下的作用。
? ? ? ? request、response兩個(gè)功能模塊分別對(duì)node的原生request、response進(jìn)行了一個(gè)功能的封裝,使用了getter和setter屬性,基于node的對(duì)象req/res對(duì)象封裝koa的request/response對(duì)象。我們基于這個(gè)原理簡(jiǎn)單實(shí)現(xiàn)一下request.js、response.js,首先創(chuàng)建request.js文件,然后寫(xiě)入以下代碼:
let url = require("url"); module.exports = { get query() { return url.parse(this.req.url, true).query; } };
? ? ? ? 這樣當(dāng)你在koa實(shí)例里使用ctx.query的時(shí)候,就會(huì)返回url.parse(this.req.url, true).query的值??丛创a可知,基于getter和setter,在request.js里還封裝了header、url、origin、path等方法,都是對(duì)原生的request上用getter和setter進(jìn)行了封裝,筆者不再這里一一實(shí)現(xiàn)。
? ? ? ? 接下來(lái)我們實(shí)現(xiàn)response.js文件代碼模塊,它和request原理一樣,也是基于getter和setter對(duì)原生response進(jìn)行了封裝,那我們接下來(lái)通過(guò)對(duì)常用的ctx.body和ctx.status這個(gè)兩個(gè)語(yǔ)句當(dāng)做例子簡(jiǎn)述一下如果實(shí)現(xiàn)koa的response的模塊,我們首先創(chuàng)建好response.js文件,然后輸入下面的代碼:
module.exports = { get body() { return this._body; }, set body(data) { this._body = data; }, get status() { return this.res.statusCode; }, set status(statusCode) { if (typeof statusCode !== "number") { throw new Error("something wrong!"); } this.res.statusCode = statusCode; } };
? ? ? ? 以上代碼實(shí)現(xiàn)了對(duì)koa的status的讀取和設(shè)置,讀取的時(shí)候返回的是基于原生的response對(duì)象的statusCode屬性,而body的讀取則是對(duì)this._body進(jìn)行讀寫(xiě)和操作。這里對(duì)body進(jìn)行操作并沒(méi)有使用原生的this.res.end,因?yàn)樵谖覀兙帉?xiě)koa代碼的時(shí)候,會(huì)對(duì)body進(jìn)行多次的讀取和修改,所以真正返回瀏覽器信息的操作是在application.js里進(jìn)行封裝和操作。
? ? ? ? 現(xiàn)在我們已經(jīng)實(shí)現(xiàn)了request.js、response.js,獲取到了request、response對(duì)象和他們的封裝的方法,然后我們開(kāi)始實(shí)現(xiàn)context.js,context的作用就是將request、response對(duì)象掛載到ctx的上面,讓koa實(shí)例和代碼能方便的使用到request、response對(duì)象中的方法?,F(xiàn)在我們創(chuàng)建context.js文件,輸入如下代碼:
let proto = {}; function delegateSet(property, name) { proto.__defineSetter__(name, function (val) { this[property][name] = val; }); } function delegateGet(property, name) { proto.__defineGetter__(name, function () { return this[property][name]; }); } let requestSet = []; let requestGet = ["query"]; let responseSet = ["body", "status"]; let responseGet = responseSet; requestSet.forEach(ele => { delegateSet("request", ele); }); requestGet.forEach(ele => { delegateGet("request", ele); }); responseSet.forEach(ele => { delegateSet("response", ele); }); responseGet.forEach(ele => { delegateGet("response", ele); }); module.exports = proto;
? ? ? ? context.js文件主要是對(duì)常用的request和response方法進(jìn)行掛載和代理,通過(guò)context.query直接代理了context.request.query,context.body和context.status代理了context.response.body與context.response.status。而context.request,context.response則會(huì)在application.js中掛載。
? ? ? ? 本來(lái)可以用簡(jiǎn)單的setter和getter去設(shè)置每一個(gè)方法,但是由于context對(duì)象定義方法比較簡(jiǎn)單和規(guī)范,在koa源碼里可以看到,koa源碼用的是__defineSetter__和__defineSetter__來(lái)代替setter/getter每一個(gè)屬性的讀取設(shè)置,這樣做主要是方便拓展和精簡(jiǎn)了寫(xiě)法,當(dāng)我們需要代理更多的res和req的方法的時(shí)候,可以向context.js文件里面的數(shù)組對(duì)象里面添加對(duì)應(yīng)的方法名和屬性名即可。
? ? ? ? 目前為止,我們已經(jīng)得到了request、response、context三個(gè)模塊對(duì)象了,接下來(lái)就是將request、response所有方法掛載到context下,讓context實(shí)現(xiàn)它的承上啟下的作用,修改application.js文件,添加如下代碼:
let http = require("http"); let context = require("./context"); let request = require("./request"); let response = require("./response"); createContext(req, res) { let ctx = Object.create(this.context); ctx.request = Object.create(this.request); ctx.response = Object.create(this.response); ctx.req = ctx.request.req = req; ctx.res = ctx.response.res = res; return ctx; }
? ? ? ? 可以看到,我們添加了createContext這個(gè)方法,這個(gè)方法是關(guān)鍵,它通過(guò)Object.create創(chuàng)建了ctx,并將request和response掛載到了ctx上面,將原生的req和res掛載到了ctx的子屬性上,往回看一下context/request/response.js文件,就能知道當(dāng)時(shí)使用的this.res或者this.response之類(lèi)的是從哪里來(lái)的了,原來(lái)是在這個(gè)createContext方法中掛載到了對(duì)應(yīng)的實(shí)例上,構(gòu)建了運(yùn)行時(shí)上下文ctx之后,我們的app.use回調(diào)函數(shù)參數(shù)就都基于ctx了。
模塊三:中間件機(jī)制和剝洋蔥模型的實(shí)現(xiàn)? ? ? ? 目前為止我們已經(jīng)成功實(shí)現(xiàn)了上下文context對(duì)象、 請(qǐng)求request對(duì)象和響應(yīng)response對(duì)象模塊,還差一個(gè)最重要的模塊,就是koa的中間件模塊,koa的中間件機(jī)制是一個(gè)剝洋蔥式的模型,多個(gè)中間件通過(guò)use放進(jìn)一個(gè)數(shù)組隊(duì)列然后從外層開(kāi)始執(zhí)行,遇到next后進(jìn)入隊(duì)列中的下一個(gè)中間件,所有中間件執(zhí)行完后開(kāi)始回幀,執(zhí)行隊(duì)列中之前中間件中未執(zhí)行的代碼部分,這就是剝洋蔥模型,koa的中間件機(jī)制。
? ? ? ? koa的剝洋蔥模型在koa1中使用的是generator + co.js去實(shí)現(xiàn)的,koa2則使用了async/await + Promise去實(shí)現(xiàn)的,接下來(lái)我們基于async/await + Promise去實(shí)現(xiàn)koa2中的中間件機(jī)制。首先,假設(shè)當(dāng)koa的中間件機(jī)制已經(jīng)做好了,那么它是能成功運(yùn)行下面代碼的:
let Koa = require("../src/application"); let app = new Koa(); app.use(async (ctx, next) => { console.log(1); await next(); console.log(6); }); app.use(async (ctx, next) => { console.log(2); await next(); console.log(5); }); app.use(async (ctx, next) => { console.log(3); ctx.body = "hello world"; console.log(4); }); app.listen(3000, () => { console.log("listenning on 3000"); });
? ? ? ? 運(yùn)行成功后會(huì)在終端輸出123456,那就能驗(yàn)證我們的koa的剝洋蔥模型是正確的。接下來(lái)我們開(kāi)始實(shí)現(xiàn),修改application.js文件,添加如下代碼: ? ? ?
compose() { return async ctx => { function createNext(middleware, oldNext) { return async () => { await middleware(ctx, oldNext); } } let len = this.middlewares.length; let next = async () => { return Promise.resolve(); }; for (let i = len - 1; i >= 0; i--) { let currentMiddleware = this.middlewares[i]; next = createNext(currentMiddleware, next); } await next(); }; } callback() { return (req, res) => { let ctx = this.createContext(req, res); let respond = () => this.responseBody(ctx); let onerror = (err) => this.onerror(err, ctx); let fn = this.compose(); return fn(ctx); }; }
? ? ? ? koa通過(guò)use函數(shù),把所有的中間件push到一個(gè)內(nèi)部數(shù)組隊(duì)列this.middlewares中,剝洋蔥模型能讓所有的中間件依次執(zhí)行,每次執(zhí)行完一個(gè)中間件,遇到next()就會(huì)將控制權(quán)傳遞到下一個(gè)中間件,下一個(gè)中間件的next參數(shù),剝洋蔥模型的最關(guān)鍵代碼是compose這個(gè)函數(shù): ? ? ?
compose() { return async ctx => { function createNext(middleware, oldNext) { return async () => { await middleware(ctx, oldNext); } } let len = this.middlewares.length; let next = async () => { return Promise.resolve(); }; for (let i = len - 1; i >= 0; i--) { let currentMiddleware = this.middlewares[i]; next = createNext(currentMiddleware, next); } await next(); }; }
? ? ? ? createNext函數(shù)的作用就是將上一個(gè)中間件的next當(dāng)做參數(shù)傳給下一個(gè)中間件,并且將上下文ctx綁定當(dāng)前中間件,當(dāng)中間件執(zhí)行完,調(diào)用next()的時(shí)候,其實(shí)就是去執(zhí)行下一個(gè)中間件。
for (let i = len - 1; i >= 0; i--) { let currentMiddleware = this.middlewares[i]; next = createNext(currentMiddleware, next); }
? ? ? ? 上面這段代碼其實(shí)就是一個(gè)鏈?zhǔn)椒聪蜻f歸模型的實(shí)現(xiàn),i是從最大數(shù)開(kāi)始循環(huán)的,將中間件從最后一個(gè)開(kāi)始封裝,每一次都是將自己的執(zhí)行函數(shù)封裝成next當(dāng)做上一個(gè)中間件的next參數(shù),這樣當(dāng)循環(huán)到第一個(gè)中間件的時(shí)候,只需要執(zhí)行一次next(),就能鏈?zhǔn)降倪f歸調(diào)用所有中間件,這個(gè)就是koa剝洋蔥的核心代碼機(jī)制。
? ? ? ? 到這里我們總結(jié)一下上面所有剝洋蔥模型代碼的流程,通過(guò)use傳進(jìn)來(lái)的中間件是一個(gè)回調(diào)函數(shù),回調(diào)函數(shù)的參數(shù)是ctx上下文和next,next其實(shí)就是控制權(quán)的交接棒,next的作用是停止運(yùn)行當(dāng)前中間件,將控制權(quán)交給下一個(gè)中間件,執(zhí)行下一個(gè)中間件的next()之前的代碼,當(dāng)下一個(gè)中間件運(yùn)行的代碼遇到了next(),又會(huì)將代碼執(zhí)行權(quán)交給下下個(gè)中間件,當(dāng)執(zhí)行到最后一個(gè)中間件的時(shí)候,控制權(quán)發(fā)生反轉(zhuǎn),開(kāi)始回頭去執(zhí)行之前所有中間件中剩下未執(zhí)行的代碼,這整個(gè)流程有點(diǎn)像一個(gè)偽遞歸,當(dāng)最終所有中間件全部執(zhí)行完后,會(huì)返回一個(gè)Promise對(duì)象,因?yàn)槲覀兊腸ompose函數(shù)返回的是一個(gè)async的函數(shù),async函數(shù)執(zhí)行完后會(huì)返回一個(gè)Promise,這樣我們就能將所有的中間件異步執(zhí)行同步化,通過(guò)then就可以執(zhí)行響應(yīng)函數(shù)和錯(cuò)誤處理函數(shù)。
? ? ? ? 當(dāng)中間件機(jī)制代碼寫(xiě)好了以后,運(yùn)行我們的上面的例子,已經(jīng)能輸出123456了,至此,我們的koa的基本框架已經(jīng)基本做好了,不過(guò)一個(gè)框架不能只實(shí)現(xiàn)功能,為了框架和服務(wù)器實(shí)例的健壯,還需要加上錯(cuò)誤處理機(jī)制。
模塊四:錯(cuò)誤捕獲和錯(cuò)誤處理? ? ? ? 要實(shí)現(xiàn)一個(gè)基礎(chǔ)框架,錯(cuò)誤處理和捕獲必不可少,一個(gè)健壯的框架,必須保證在發(fā)生錯(cuò)誤的時(shí)候,能夠捕獲到錯(cuò)誤和拋出的異常,并反饋出來(lái),將錯(cuò)誤信息發(fā)送到監(jiān)控系統(tǒng)上進(jìn)行反饋,目前我們實(shí)現(xiàn)的簡(jiǎn)易koa框架還沒(méi)有能實(shí)現(xiàn)這一點(diǎn),我們接下加上錯(cuò)誤處理和捕獲的機(jī)制。
throw new Error("oooops");
? ? ? ? 基于現(xiàn)在的框架,如果中間件代碼中出現(xiàn)如上錯(cuò)誤異常拋出,是捕獲不到錯(cuò)誤的,這時(shí)候我們看一下application.js中的callback函數(shù)的return返回代碼,如下:
return fn(ctx).then(respond);
? ? ? ? 可以看到,fn是中間件的執(zhí)行函數(shù),每一個(gè)中間件代碼都是由async包裹著的,而且中間件的執(zhí)行函數(shù)compose返回的也是一個(gè)async函數(shù),我們根據(jù)es7的規(guī)范知道,async返回的是一個(gè)promise的對(duì)象實(shí)例,我們?nèi)绻胍东@promise的錯(cuò)誤,只需要使用promise的catch方法,就可以把所有的中間件的異常全部捕獲到,修改后callback的返回代碼如下:
return fn(ctx).then(respond).catch(onerror);
? ? ? ? 現(xiàn)在我們已經(jīng)實(shí)現(xiàn)了中間件的錯(cuò)誤異常捕獲,但是我們還缺少框架層發(fā)生錯(cuò)誤的捕獲機(jī)制,我們希望我們的服務(wù)器實(shí)例能有錯(cuò)誤事件的監(jiān)聽(tīng)機(jī)制,通過(guò)on的監(jiān)聽(tīng)函數(shù)就能訂閱和監(jiān)聽(tīng)框架層面上的錯(cuò)誤,實(shí)現(xiàn)這個(gè)機(jī)制不難,使用nodejs原生events模塊即可,events模塊給我們提供了事件監(jiān)聽(tīng)on函數(shù)和事件觸發(fā)emit行為函數(shù),一個(gè)發(fā)射事件,一個(gè)負(fù)責(zé)接收事件,我們只需要將koa的構(gòu)造函數(shù)繼承events模塊即可,構(gòu)造后的偽代碼如下:
let EventEmitter = require("events"); class Application extends EventEmitter {}
? ? ? ? 繼承了events模塊后,當(dāng)我們創(chuàng)建koa實(shí)例的時(shí)候,加上on監(jiān)聽(tīng)函數(shù),代碼如下:
let app = new Koa(); app.on("error", err => { console.log("error happends: ", err.stack); });
? ? ? ? 這樣我們就實(shí)現(xiàn)了框架層面上的錯(cuò)誤的捕獲和監(jiān)聽(tīng)機(jī)制了??偨Y(jié)一下,錯(cuò)誤處理和捕獲,分中間件的錯(cuò)誤處理捕獲和框架層的錯(cuò)誤處理捕獲,中間件的錯(cuò)誤處理用promise的catch,框架層面的錯(cuò)誤處理用nodejs的原生模塊events,這樣我們就可以把一個(gè)服務(wù)器實(shí)例上的所有的錯(cuò)誤異常全部捕獲到了。至此,我們就完整實(shí)現(xiàn)了一個(gè)輕量版的koa框架了。
結(jié)尾目前為止,我們已經(jīng)實(shí)現(xiàn)了一個(gè)輕量版的koa框架了,我們實(shí)現(xiàn)了封裝node http server、創(chuàng)建Koa類(lèi)構(gòu)造函數(shù)、構(gòu)造request、response、context對(duì)象、中間件機(jī)制和剝洋蔥模型的實(shí)現(xiàn)、錯(cuò)誤捕獲和錯(cuò)誤處理這四個(gè)大模塊,理解了這個(gè)輕量版koa的實(shí)現(xiàn)原理,再去看koa2的源碼,你就會(huì)發(fā)現(xiàn)一切都豁然開(kāi)朗,koa2的源碼無(wú)非就是在這個(gè)輕量版基礎(chǔ)上加了很多工具函數(shù)和細(xì)節(jié)的處理,限于篇幅筆者就不再一一介紹了。
本文實(shí)現(xiàn)的輕量版koa的完整代碼gitlab地址為:article_koa2?
作者:第一名的小蝌蚪 github: 文章會(huì)第一時(shí)間分享在前端屌絲心路歷程,歡迎star或者watch,感恩?最后,TNFE團(tuán)隊(duì)為前端開(kāi)發(fā)人員整理出了小程序以及web前端技術(shù)領(lǐng)域的最新優(yōu)質(zhì)內(nèi)容,每周更新?,歡迎star,github地址:https://github.com/Tnfe/TNFE-...
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://systransis.cn/yun/109171.html
摘要:實(shí)現(xiàn)的四大模塊上文簡(jiǎn)述了源碼的大體框架結(jié)構(gòu),接下來(lái)我們來(lái)實(shí)現(xiàn)一個(gè)的框架,筆者認(rèn)為理解和實(shí)現(xiàn)一個(gè)框架需要實(shí)現(xiàn)四個(gè)大模塊,分別是封裝創(chuàng)建類(lèi)構(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)、自由度...
摘要:?jiǎn)?dòng)流程主要的啟動(dòng)流程就是下面的步引入包實(shí)例化編寫(xiě)中間件監(jiān)聽(tīng)服務(wù)器引入包引入包其實(shí)就是引入的一個(gè)繼承于原生的類(lèi)的類(lèi)其中就包含了等原型方法實(shí)例化執(zhí)行,將等對(duì)象封裝在實(shí)例中編寫(xiě)中間件首先判斷的類(lèi)型,不是方法直接拋錯(cuò)是生成器函數(shù)的話用封裝是函數(shù) 啟動(dòng)流程 koa 主要的啟動(dòng)流程就是下面的 4 步:引入 koa 包 => 實(shí)例化 koa => 編寫(xiě)中間件 => 監(jiān)聽(tīng)服務(wù)器 const koa ...
摘要:使用的中間件是一個(gè)簡(jiǎn)潔的框架,把許多小功能都拆分成了中間件,用一個(gè)洋蔥模型保證了中間件豐富的可拓展性,我們要使用來(lái)保持登錄狀態(tài),就需要引用中間件。默認(rèn)是過(guò)期時(shí)間,以毫秒為單位計(jì)算。自動(dòng)提交到響應(yīng)頭。默認(rèn)是是否在快過(guò)期時(shí)刷新的有效期。 項(xiàng)目要用到登錄注冊(cè),就需要使用到Cookie和Session來(lái)保持登錄狀態(tài),于是就簡(jiǎn)單研究了一下 Cookie和Session的工作原理 前面已經(jīng)專(zhuān)門(mén)發(fā)過(guò)...
摘要:云集一線大廠有真正實(shí)力的程序員團(tuán)隊(duì)云集一線大廠經(jīng)驗(yàn)豐厚的碼農(nóng),開(kāi)源奉獻(xiàn)各教程。融合多種常見(jiàn)的需求場(chǎng)景網(wǎng)絡(luò)請(qǐng)求解析模板引擎靜態(tài)資源日志記錄錯(cuò)誤請(qǐng)求處理。結(jié)合語(yǔ)句中轉(zhuǎn)中間件控制權(quán),解決回調(diào)地獄問(wèn)題。注意分支中的目錄為當(dāng)節(jié)課程后的完整代碼。 ?? ?與眾不同的學(xué)習(xí)方式,為你打開(kāi)新的編程視角 獨(dú)特的『同步學(xué)習(xí)』方式 文案講解+視頻演示,文字可激發(fā)深層的思考、視頻可還原實(shí)戰(zhàn)操作過(guò)程。 云...
閱讀 3961·2021-11-17 09:33
閱讀 3298·2021-10-08 10:05
閱讀 3126·2021-09-22 15:36
閱讀 1155·2021-09-06 15:02
閱讀 2782·2019-08-29 12:45
閱讀 1606·2019-08-26 13:40
閱讀 3414·2019-08-26 13:37
閱讀 436·2019-08-26 13:37