摘要:前言目前最新版本是所以本文分析也基于這個(gè)版本。源碼分析直接切入主題由于目前是一個(gè)獨(dú)立的路由和中間件框架。所以分析的方向也以這兩個(gè)為主。源碼去年的時(shí)候有分析過現(xiàn)在對比分析思考下。
前言
目前express最新版本是4.16.2,所以本文分析也基于這個(gè)版本。目前從npm倉庫上來看express使用量挺高的,express月下載量約為koa的40倍。所以目前研究下express還是有一定意義的。
源碼分析直接切入主題,由于目前express是一個(gè)獨(dú)立的路由和中間件web框架。所以分析的方向也以這兩個(gè)為主。源碼的研究只注重關(guān)鍵步驟和流程思想, 具體的hack,異常,邊界處理不做過多精力關(guān)注。
關(guān)鍵步驟 中間件的執(zhí)行/** * //負(fù)責(zé)具體請求的邏輯處理,當(dāng)一維layer中所對應(yīng)的stack執(zhí)行完后執(zhí)行done(目的是為了一維路由列表中進(jìn)行下一個(gè)路由的匹配執(zhí)行)。 * @param done(路由中的next) */ Route.prototype.dispatch = function dispatch(req, res, done) { var idx = 0; var stack = this.stack; next(); //遞歸方式執(zhí)行stack中的layer,通過next控制流程的執(zhí)行 function next(err) { // 出錯(cuò)直接退出當(dāng)前stack和路由列表中回調(diào)的后續(xù)執(zhí)行 if (err && err === "router") { return done(err) } //出錯(cuò)直接退出當(dāng)前stack列表后續(xù)執(zhí)行,進(jìn)行下一個(gè)路由匹配 if (err && err === "route") { return done(); } var layer = stack[idx++]; //執(zhí)行結(jié)束 if (!layer) { return done(err); } if (layer.method && layer.method !== method) { return next(err); } //調(diào)用具體注冊好的邏輯 if (err) { layer.handle_error(err, req, res, next); } else { layer.handle_request(req, res, next); } } };
說明 上面是源碼的部分摘要,去除了無關(guān)的信息,對關(guān)鍵步驟加了注解。
路由匹配/** * 對具體請求進(jìn)行路由分發(fā)處理 * out 是最后一個(gè)處理器,默認(rèn)是請求的回調(diào),不傳的話是內(nèi)部提供的error handlers */ proto.handle = function handle(req, res, out) { var self = this; var idx = 0; var paramcalled = {}; // middleware 和 roues var stack = self.stack; var done = restore(out, req, "baseUrl", "next", "params"); next(); //遞歸方式遍歷注冊的一維路由 function next(err) { var layerError = err === "route" ? null : err; var layer,match,route; //取出注冊好的路由,進(jìn)行請求匹配 while (match !== true && idx < stack.length) { layer = stack[idx++]; //req的path匹配,如果有注冊參數(shù)路由會(huì)解析參數(shù)到layer.params上 match = matchLayer(layer, path); route = layer.route; } if (match !== true) { return done(layerError); } //將解析好的路徑參數(shù)放到請求對象上,以便后續(xù)參數(shù)回調(diào)邏輯的使用 req.params = layer.params; //路徑參數(shù)回調(diào)回調(diào) ,例如請求 /user/1 ,參數(shù)回調(diào) app.param("user",cb1) app.get("/user/:user",cb2) 會(huì)先執(zhí)行注冊的param cb1,然后才會(huì)是router中注冊的cb2 self.process_params(layer, paramcalled, req, res, function (err) { if (err) { return next(layerError || err); } if (route) { return layer.handle_request(req, res, next); } }); } };
說明 上面摘取了請求進(jìn)行路由分發(fā)處理的關(guān)鍵步驟,并做了相應(yīng)的注解。
執(zhí)行流程當(dāng)一個(gè)請求過來的時(shí)候交給handler方法,進(jìn)行路由的匹配,以遞歸的方式遍歷(路由匹配一節(jié)中介紹過),當(dāng)匹配到某一個(gè)路由的時(shí)候在dispatch執(zhí)行,web應(yīng)用啟動(dòng)初始化前注冊好的回調(diào)邏輯,執(zhí)行的方式也是以遞歸的方式(中間件執(zhí)行一節(jié)中介紹過)。
請求匹配執(zhí)行邏輯已經(jīng)介紹過了,下面結(jié)合著初始化的邏輯,進(jìn)行分析,具體如下圖所示。
說明
初始化針對上圖做一下說明,啟動(dòng)服務(wù)應(yīng)用的時(shí)候先進(jìn)行初始化,注冊"/jsdt"的get請求,并給路由綁定相應(yīng)的回調(diào)。其中我們的路由放在一維的layer中,每一層路由,例如"/jsdt"又對應(yīng)一個(gè)處理列表,這個(gè)二維列表里又存儲(chǔ)著一系列l(wèi)ayer(具體的回調(diào)處理邏輯),因?yàn)橐痪Slayer中已經(jīng)記錄了路徑"/jsdt",所以二維的layer中就不用記錄路徑了,給個(gè)默認(rèn)值"/",以保持一維layer和二維layer中結(jié)構(gòu)的統(tǒng)一。
請求當(dāng)一個(gè)請求過來的時(shí)候先去一維layer所存儲(chǔ)的路由中進(jìn)行路徑匹配,以遞歸的方式。匹配到了,通過dispatch方法,在執(zhí)行路由所對應(yīng)的二維layer中的回調(diào)邏輯,也是以遞歸的方式執(zhí)行,在遞歸的過程中,如果發(fā)生異常,通過路由中傳遞過來的out,直接進(jìn)行下一個(gè)路由的匹配。
/** *遞歸執(zhí)行stack中的handle * @param out 路由中的next */ Route.prototype.dispatch = function (req, res, out) (xxx)
當(dāng)執(zhí)行遇到res.end()的時(shí)候,數(shù)據(jù)響應(yīng)完后,整個(gè)請求響應(yīng)過程就結(jié)束了。
koa vs expresskao源碼去年的時(shí)候有分析過,現(xiàn)在對比分析思考下。express中間件和koa中間件區(qū)別,以及與redux中間件區(qū)別 ?
koa比較迷你,微內(nèi)核,拓展性強(qiáng),所以一些web框架例如阿里的eggjs就是基于koa。而express集成了路由和static中間件所以顯得重一些。
網(wǎng)上很多文章都說一個(gè)是線性的,一個(gè)是洋蔥模型。因?yàn)閮蓚€(gè)我都研究過,我覺的這種說法不對,其實(shí)執(zhí)行的時(shí)候都是洋蔥形。主要的區(qū)別是koa內(nèi)核底層原生支持async語法,koa中的middlewares經(jīng)過compose以后,每次執(zhí)行到await next返回的都是一個(gè)promise,所以我們可以在頂層加一個(gè)try……catch進(jìn)行異常捕獲,這算是koa比較方便的一點(diǎn)。還有一點(diǎn)很多人提到過說koa可以記錄處理時(shí)間,那是因?yàn)槊看蝦es.body賦值的時(shí)候并沒有res.end,所以在第一個(gè)中間件很容易記錄處理的時(shí)間,如下所示,在中間件執(zhí)行完后,在handleResponse中才會(huì)將請求處理結(jié)果返回給客戶端。
fn(ctx)[是一個(gè)立即狀態(tài)的promise].then(handleResponse).catch(onerror);
而express每次res.send的時(shí)候數(shù)據(jù)已經(jīng)發(fā)給客戶端了,當(dāng)然也可以實(shí)現(xiàn)這種需求,只不過沒有koa方便。多說幾句,其實(shí)java中也有類似的實(shí)現(xiàn),例如java中的aop,過濾器,將通用的邏輯,例如日志,權(quán)限等模塊通過配置的方式進(jìn)行靈活的插入,配置在xml中。比較靈活,每個(gè)模塊之間互相解耦,根據(jù)需求實(shí)現(xiàn)可插拔效果。
在說一下與react中redux中間件機(jī)制區(qū)別,redux中的applyMiddleware中間件機(jī)制,可以在處理store前后加一些通用處理,利用高階函數(shù)compose,通過reduce將多個(gè)函數(shù)組合成一個(gè)可執(zhí)行執(zhí)行函數(shù)
//applyMiddleware.js chain = middlewares.map(middleware => middleware(middlewareAPI)) dispatch = compose(...chain)(store.dispatch)
//compose.js export default function compose(...funcs) { return funcs.reduce((a, b) => (...args) => a(b(...args))) }
組合好最后執(zhí)行的時(shí)候是洋蔥模型效果,舉個(gè)例子 compose(a, b, c)變成 a(b(c())),而a,b,c的結(jié)構(gòu)類似下面這種形式
function fnA(next) { return function() { console.log("fnA start") next() console.log("fnA end") } }
所以a(b(c()))()執(zhí)行的時(shí)候,就會(huì)通過next來控制調(diào)用下一個(gè)中間件,整體的執(zhí)行等價(jià)于express遞歸調(diào)用方式
express路由和 vue路由共同點(diǎn)vue中也有路由,官方的vue-router,他解決的是url和模板組件匹配渲染的問題, 而express中解決的url和handler匹配執(zhí)行的問題,而koa內(nèi)核里面沒有集成路由。 從vue-router和express路由中可以看出路由的共性,是為了解決路徑和相應(yīng)處理邏輯的匹配問題在web開發(fā)中。
toy版本express根據(jù)上述分析的邏輯,實(shí)現(xiàn)了一個(gè)簡化版的express ,融入了express的核心思想,有詳盡的步驟注釋,有需要的可以參考下。
參考源碼版本說明
express 4.16.2
koa 2.2
redux 3.7.2
參考鏈接
https://github.com/koajs/koa/...
https://www.reddit.com/r/node...
https://blog.jscrambler.com/m...
文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請注明本文地址:http://m.hztianpu.com/yun/93403.html
摘要:以前其實(shí)寫過一篇和的對比但是后來發(fā)現(xiàn)里面有不少謬誤所以一直惦記著糾正一下之前的錯(cuò)誤尤其關(guān)于中間件部分的對比這里的就拿更加簡單的代替的執(zhí)行流程通常我們都說的中間件模型是線性的也就是一個(gè)一個(gè)往下執(zhí)行的如下圖這么說當(dāng)然是沒錯(cuò)的但是當(dāng)我們執(zhí)行下面代 以前其實(shí)寫過一篇express和koa的對比, 但是后來發(fā)現(xiàn)里面有不少謬誤. 所以一直惦記著糾正一下之前的錯(cuò)誤, 尤其關(guān)于中間件部分的對比. 這里...
摘要:我們分別使用這樣的原則來測試向每個(gè)架構(gòu)注入個(gè)靜態(tài)路由,測試最末尾的那個(gè)。而我們?nèi)绾巫龅竭_(dá)到的性能,主要我們在內(nèi)存中維護(hù)了一份靜態(tài)路由列表,能讓程序以最快的速度找到我們需要的。 對比 如果使用nodejs來搭建Service服務(wù),那么我們首選express或者koa,而fastify告訴我們一個(gè)數(shù)據(jù): Framework Version Router? Requests/sec ...
摘要:使用承諾和異步功能來擺脫回調(diào)地獄的應(yīng)用程序,并簡化錯(cuò)誤處理。它暴露了自己的和對象,而不是的和對象。因此,可被視為的模塊的抽象,其中是的應(yīng)用程序框架。這使得中間件對于整個(gè)堆棧而言不僅僅是最終應(yīng)用程序代碼,而且更易于書寫,并更不容易出錯(cuò)。 Koa 與 Express 此系列文章的應(yīng)用示例已發(fā)布于 GitHub: koa-docs-Zh-CN. 可以 Fork 幫助改進(jìn)或 Star 關(guān)注更新...
摘要:前言一直混跡社區(qū)突然發(fā)現(xiàn)自己收藏了不少好文但是管理起來有點(diǎn)混亂所以將前端主流技術(shù)做了一個(gè)書簽整理不求最多最全但求最實(shí)用。 前言 一直混跡社區(qū),突然發(fā)現(xiàn)自己收藏了不少好文但是管理起來有點(diǎn)混亂; 所以將前端主流技術(shù)做了一個(gè)書簽整理,不求最多最全,但求最實(shí)用。 書簽源碼 書簽導(dǎo)入瀏覽器效果截圖showImg(https://segmentfault.com/img/bVbg41b?w=107...
摘要:的網(wǎng)站仍然使用有漏洞庫上周發(fā)布了開源社區(qū)安全現(xiàn)狀報(bào)告,發(fā)現(xiàn)隨著開源社區(qū)的日漸活躍,開源代碼中包含的安全漏洞以及影響的范圍也在不斷擴(kuò)大。與應(yīng)用安全是流行的服務(wù)端框架,本文即是介紹如何使用以及其他的框架來增強(qiáng)應(yīng)用的安全性。 showImg(https://segmentfault.com/img/remote/1460000012181337?w=1240&h=826); 前端每周清單專注...
閱讀 1716·2021-09-08 10:42
閱讀 3671·2021-08-11 10:23
閱讀 4181·2019-08-30 14:10
閱讀 2795·2019-08-29 17:29
閱讀 3154·2019-08-29 12:50
閱讀 695·2019-08-26 13:36
閱讀 3521·2019-08-26 11:59
閱讀 1549·2019-08-23 16:23