摘要:目前使用人數(shù)眾多。通過利用函數(shù),幫你丟棄回調(diào)函數(shù),并有力地增強錯誤處理。這個系列的博客主要講解和的中間件機制,本篇將主要講解的中間件機制。其中間件機制的核心為內(nèi)部方法的實現(xiàn)。
提到 Node.js 開發(fā),不得不提目前炙手可熱的2大框架 Express 和 Koa。Express 是一個保持最小規(guī)模的靈活的 Node.js Web 應用程序開發(fā)框架,為 Web 和移動應用程序提供一組強大的功能。目前使用人數(shù)眾多。
Koa 是一個新的 web 框架,由 Express 幕后的原班人馬打造, 致力于成為 web 應用和 API 開發(fā)領域中的一個更小、更富有表現(xiàn)力、更健壯的基石。 通過利用 async 函數(shù),Koa 幫你丟棄回調(diào)函數(shù),并有力地增強錯誤處理。 Koa 并沒有捆綁任何中間件, 而是提供了一套優(yōu)雅的方法,幫助您快速而愉快地編寫服務端應用程序。
相信對這兩大框架有一些了解的人都或多或少的會了解其中間件機制,Express 為線型模型,而 Koa 則為洋蔥型模型。這個系列的博客主要講解 Express 和 Koa 的中間件機制,本篇將主要講解 Express 的中間件機制。
Express 中間件connect 曾經(jīng)是 express 3.x 之前的核心,而 express 4.x 已經(jīng)把 connect 移除,在 express 中自己實現(xiàn)了 connect 的接口,所以我們本篇中的源碼解釋將直接使用 connect 源碼。
示例下面將使用 Express 實現(xiàn)一個簡單的 demo 來進行中間件機制的講解
var express = require("express"); var app = express(); app.use(function (req, res, next) { console.log("第一個中間件start"); setTimeout(() => { next(); }, 1000) console.log("第一個中間件end"); }); app.use(function (req, res, next) { console.log("第二個中間件start"); setTimeout(() => { next(); }, 1000) console.log("第二個中間件end"); }); app.use("/foo", function (req, res, next) { console.log("接口邏輯start"); next(); console.log("接口邏輯end"); }); app.listen(4000);
此時的輸出比較符合我們對 Express 線性的理解,其輸出為
第一個中間件start 第一個中間件end 第二個中間件start 第二個中間件end 接口邏輯start 接口邏輯end
但是,如果我們?nèi)∠糁虚g件內(nèi)部的異步處理直接調(diào)用 next()
var express = require("express"); var app = express(); app.use(function (req, res, next) { console.log("第一個中間件start"); next() console.log("第一個中間件end"); }); app.use(function (req, res, next) { console.log("第二個中間件start"); next() console.log("第二個中間件end"); }); app.use("/foo", function (req, res, next) { console.log("接口邏輯start"); next(); console.log("接口邏輯end"); }); app.listen(4000);
輸出結果為
第一個中間件start 第二個中間件start 接口邏輯start 接口邏輯end 第二個中間件end 第一個中間件end
這種結果不是和 Koa 的輸出很相似嗎?是的,但是它和剝洋蔥模型還是不一樣的,其實這種輸出的結果是由于代碼的同步運行導致的,并不是說 Express 不是線性的模型。
當我們的中間件內(nèi)沒有進行異步操作時,其實我們的代碼最后是以下面這種方式運行的
app.use(function middleware1(req, res, next) { console.log("第一個中間件start") // next() (function (req, res, next) { console.log("第二個中間件start") // next() (function (req, res, next) { console.log("接口邏輯start") // next() (function handler(req, res, next) { // do something })() console.log("接口邏輯end") })() console.log("第二個中間件end") })() console.log("第一個中間件end") })
導致這種運行方式的其實就是 connect 的實現(xiàn)方式,接下來我們進行其源碼的解析
connect 源碼解析connect的源碼僅有200多行,但是這里講解只選擇其中部分核心代碼,并非完整代碼,查看全部代碼請移步 github
中間件的掛載主要依賴 proto.use 和 proto.handle,這里我們刪掉部分 if 判斷以使我們更專注于其內(nèi)部原理的實現(xiàn)
proto.use = function use(route, fn) { var handle = fn; var path = route; // 這里是對直接填入回調(diào)函數(shù)的進行容錯處理 // default route to "/" if (typeof route !== "string") { handle = route; path = "/"; } . . . this.stack.push({ route: path, handle: handle }); return this; };
proto.use 主要將我們需要掛載的中間件存儲在其自身 stack 屬性上,同時進行部分兼容處理,這一塊比較容易理解。其中間件機制的核心為 proto.handle 內(nèi)部 next 方法的實現(xiàn)。
proto.handle = function handle(req, res, out) { var index = 0; var stack = this.stack; function next(err) { // next callback var layer = stack[index++]; // all done if (!layer) { defer(done, err); return; } // route data var path = parseUrl(req).pathname || "/"; var route = layer.route; // skip this layer if the route doesn"t match if (path.toLowerCase().substr(0, route.length) !== route.toLowerCase()) { return next(err); } // call the layer handle call(layer.handle, route, err, req, res, next); } next(); };
在刪除到部分非核心代碼后,可以清晰的看到,proto.handle 的核心就是 next 方法的實現(xiàn)和遞歸調(diào)用,對存在于 stack 中的中間件取出、執(zhí)行。
這里便可以解釋上文中異步和非異步過程中所輸出的結果的差異了。
當有異步代碼時,將會直接跳過繼續(xù)執(zhí)行,此時的 next 方法并未執(zhí)行,需要等待當前隊列中的事件全部執(zhí)行完畢,所以此時我們輸出的數(shù)據(jù)是線性的。
當 next 方法直接執(zhí)行時,本質(zhì)上所有的代碼都已經(jīng)為同步,所以層層嵌套,最外層的肯定會在最后,輸出了類似剝洋蔥模型的結果。
總結connect 的實現(xiàn)其基本原理是維護一個 stack 數(shù)組,將所需要掛載的中間件處理后全部 push 到數(shù)組內(nèi),之后在數(shù)組內(nèi)循環(huán)執(zhí)行 next 方法,直至所有中間件掛載完畢,當然這過程中會做一些異常、兼容等的處理。
文章版權歸作者所有,未經(jīng)允許請勿轉載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/102523.html
摘要:前言目前最新版本是所以本文分析也基于這個版本。源碼分析直接切入主題由于目前是一個獨立的路由和中間件框架。所以分析的方向也以這兩個為主。源碼去年的時候有分析過現(xiàn)在對比分析思考下。 前言 目前express最新版本是4.16.2,所以本文分析也基于這個版本。目前從npm倉庫上來看express使用量挺高的,express月下載量約為koa的40倍。所以目前研究下express還是有一定意義...
摘要:在我眼中,的確是比輕量的多,給我的感覺更像是一個中間件框架,只是一個基礎的架子,需要用到的相應的功能時,用相應的中間件來實現(xiàn)就好,諸如路由系統(tǒng)等。基于的庫,所以利用來代替回調(diào),而由于對的支持,所以利用的是。 koa koa是由express原班人馬打造的一個更小、更富有表現(xiàn)力、更健壯的web框架。 在我眼中,koa的確是比express輕量的多,koa給我的感覺更像是一個中間件框架,k...
摘要:現(xiàn)在我們從實現(xiàn)一個簡易的方法開始探索其中的機制。其中內(nèi)部的可以將上一個的返回值傳遞給外部。一言以蔽之實現(xiàn)了遞歸調(diào)用的方法。當執(zhí)行到的中間件沒有時并且返回的為時逆序執(zhí)行。 本文發(fā)布在github.com/ssssyoki,歡迎star,issues共同交流。 Koa是基于Node.js的下一代web開發(fā)框架,相比Express更輕,源碼只有幾百行。與傳統(tǒng)的中間件不同,在Koa 1.x中采...
摘要:洋蔥圈處理模型。基于的靈活強大的中間件機制。參考官網(wǎng)提供的基本,不在贅述部分實現(xiàn),參考源碼分析常用服務端口監(jiān)聽返回適用于方法的回調(diào)函數(shù)來處理請求。 本文 github 地址: https://github.com/HCThink/h-blog/blob/master/source/koa2/readme.md github 首頁(star+watch,一手動態(tài)直達): https:...
摘要:以前其實寫過一篇和的對比但是后來發(fā)現(xiàn)里面有不少謬誤所以一直惦記著糾正一下之前的錯誤尤其關于中間件部分的對比這里的就拿更加簡單的代替的執(zhí)行流程通常我們都說的中間件模型是線性的也就是一個一個往下執(zhí)行的如下圖這么說當然是沒錯的但是當我們執(zhí)行下面代 以前其實寫過一篇express和koa的對比, 但是后來發(fā)現(xiàn)里面有不少謬誤. 所以一直惦記著糾正一下之前的錯誤, 尤其關于中間件部分的對比. 這里...
閱讀 1611·2023-04-25 16:29
閱讀 949·2021-11-15 11:38
閱讀 2284·2021-09-23 11:45
閱讀 1410·2021-09-22 16:03
閱讀 2532·2019-08-30 15:54
閱讀 1198·2019-08-30 10:53
閱讀 2599·2019-08-29 15:24
閱讀 1095·2019-08-26 12:25