摘要:接下來(lái)通過(guò)研究源碼,來(lái)探討路由原理的實(shí)現(xiàn)。類保存和一些數(shù)據(jù)信息相同點(diǎn)都是存放掛載路徑用來(lái)判斷是否是路由中間件。放在里的路由中間件,通過(guò)指向,與相關(guān)聯(lián)起來(lái)
Express
基于 Node.js 平臺(tái),快速、開放、極簡(jiǎn)的 web 開發(fā)框架安裝
//應(yīng)用生成器工具 npm install express-generator -g //創(chuàng)建express應(yīng)用包 express app //安裝依賴 npm install
成功生成后,會(huì)產(chǎn)生以下的目錄和文件:
|---bin |---node_module |---public |---routes |---view |---app.js |---package.json
接下來(lái)我們通過(guò):
npm start
啟動(dòng)程序后,訪問(wèn)127.0.0.1:3000,就能訪問(wèn)到express的頁(yè)面了。
接下來(lái)通過(guò)研究源碼,來(lái)探討express路由原理的實(shí)現(xiàn)。
路由我們通過(guò)查看app.js和index.js文件:
app.js
var index = require("./routes/index"); app.use("/", index); //或 app.get("/", index);
routes/index.js
var express = require("express"); var router = express.Router(); router.get("/", function(req, res, next) { res.render("index", { title: "Express" }); });
可以看出,express的路由大概實(shí)現(xiàn) 定義一份路由規(guī)則文件,再通過(guò)app.use()或者app[METHOD]來(lái)建立路由規(guī)則訪問(wèn)聯(lián)系,雖然兩者的結(jié)果一樣,但是存在本質(zhì)上的區(qū)別。
下圖是主要涉及的幾個(gè)文件:
接下來(lái)我們通過(guò)源碼首先看看app.use()具體是一個(gè)什么樣實(shí)現(xiàn)思路。
app.use我們打開node_module里的express文件夾。打開lib/application.js文件。
app.use = function use(fn) { var offset = 0; var path = "/"; // default path to "/" // disambiguate app.use([fn]) if (typeof fn !== "function") { var arg = fn; while (Array.isArray(arg) && arg.length !== 0) { arg = arg[0]; } // first arg is the path if (typeof arg !== "function") { offset = 1; path = fn; } } var fns = flatten(slice.call(arguments, offset)); if (fns.length === 0) { throw new TypeError("app.use() requires middleware functions"); } // setup router this.lazyrouter(); var router = this._router; fns.forEach(function(fn) { // non-express app if (!fn || !fn.handle || !fn.set) { return router.use(path, fn); } debug(".use app under %s", path); fn.mountpath = path; fn.parent = this; // restore .app property on req and res router.use(path, function mounted_app(req, res, next) { var orig = req.app; fn.handle(req, res, function(err) { setPrototypeOf(req, orig.request) setPrototypeOf(res, orig.response) next(err); }); }); // mounted an app fn.emit("mount", this); }, this); return this; };
看到use里部分的代碼,開始做了判斷處理use掛載的是路徑還是function,并且通過(guò)lazyrouter()方法實(shí)例router類,并且全局只存在一個(gè)router實(shí)例對(duì)象,最終調(diào)用router.use()方法。
接著,我們到lib/router/index.js 看router.use方法的實(shí)現(xiàn):
proto.use = function use(fn) { var offset = 0; var path = "/"; // default path to "/" // disambiguate router.use([fn]) if (typeof fn !== "function") { var arg = fn; while (Array.isArray(arg) && arg.length !== 0) { arg = arg[0]; } // first arg is the path if (typeof arg !== "function") { offset = 1; path = fn; } } var callbacks = flatten(slice.call(arguments, offset)); if (callbacks.length === 0) { throw new TypeError("Router.use() requires middleware functions"); } for (var i = 0; i < callbacks.length; i++) { var fn = callbacks[i]; if (typeof fn !== "function") { throw new TypeError("Router.use() requires middleware function but got a " + gettype(fn)); } // add the middleware debug("use %o %s", path, fn.name || "") var layer = new Layer(path, { sensitive: this.caseSensitive, strict: false, end: false }, fn); layer.route = undefined; this.stack.push(layer); } return this; };
通過(guò)對(duì)比app.use方法,router.use前半部分處理相同,但后面實(shí)例化一個(gè)Layer類,并且丟進(jìn)stack里。
Layer類保存Router和Route一些數(shù)據(jù)信息:
相同點(diǎn):
path都是存放掛載路徑,options.end用來(lái)判斷是否是路由中間件。
不同點(diǎn):
Router和Route的區(qū)別是一個(gè)是添非路由中間件,另一個(gè)是添加路由中間件。
他們的layer.route指向也不一樣,一個(gè)指向undefined,另一個(gè)沒有route屬性。
文章進(jìn)行到一半,我們小總結(jié)一下,app.use()方法是用來(lái)添加非路由中間件的,最終是調(diào)用router實(shí)例方法,會(huì)實(shí)例劃一個(gè)Layer類對(duì)象用于存放數(shù)據(jù),并且把layer對(duì)象push進(jìn)router.stack里,全局只有一個(gè)router。
app[METHOD]我們通過(guò)源碼去探討路由中間件app[METHOD]是一個(gè)怎樣的原理:
在app.js把app.use("",index)改成app.get("",index).
application.js:
methods.forEach(function(method) { app[method] = function(path) { if (method === "get" && arguments.length === 1) { // app.get(setting) return this.set(path); } this.lazyrouter(); var route = this._router.route(path); route[method].apply(route, slice.call(arguments, 1)); return this; }; });
可以看出,代碼里做了一個(gè)app.get方法的判斷處理,get方法只有一個(gè)參數(shù)時(shí),是獲取app的本地變量,后面還是實(shí)例化router對(duì)象,并且用router上的route方法放回的對(duì)象去調(diào)用Route類上的route[METHOD].
/lib/router/route.js
methods.forEach(function(method){ Route.prototype[method] = function(){ var handles = flatten(slice.call(arguments)); for (var i = 0; i < handles.length; i++) { var handle = handles[i]; if (typeof handle !== "function") { var type = toString.call(handle); var msg = "Route." + method + "() requires callback functions but got a " + type; throw new Error(msg); } debug("%s %o", method, this.path) var layer = Layer("/", {}, handle); layer.method = method; this.methods[method] = true; this.stack.push(layer); } return this; }; });
在route里有一個(gè)實(shí)例化的layer,且放在stack里,與Router的layer不同的是,Route的沒有l(wèi)ayer.route且layer.method存放http方法。
到這里,我們大概可以總結(jié)下路由中間件和非路由中間件的聯(lián)系,如下圖:
app初始化時(shí),會(huì)push兩個(gè)方法(init,query)進(jìn)router.stack里。我們可以通過(guò)app.use往app添加非路由中間件,也可以通過(guò)app[METHOD]添加路由中間件,同樣是push layer實(shí)例對(duì)象,但route是指向Route實(shí)例化的對(duì)象。
完整的Router邏輯過(guò)程,如圖:
總結(jié)express中添加中間件方法有app.use和app[METHOD],當(dāng)然還有內(nèi)置的Router類,app.use用來(lái)添加非路由中間件,app[METHOD]用來(lái)添加路由中間件。
Layer類封裝中間的path和handle(fns的處理)
Router和Route都有對(duì)應(yīng)的stack,但是Route在整個(gè)app中只有一個(gè),而Route可以又多個(gè)。放在Router
stack里的路由中間件,通過(guò)Layer.route指向Route,與Route stack相關(guān)聯(lián)起來(lái)
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://systransis.cn/yun/93337.html
摘要:是下的一個(gè)優(yōu)秀的框架,但是使用后,在流量增長(zhǎng)時(shí),進(jìn)程有時(shí)突然內(nèi)存暴漲保持高占用。如果是內(nèi)存泄露引起的,則需要細(xì)心檢查代碼,確定變量能正?;厥?。每個(gè)對(duì)象有自己產(chǎn)生的內(nèi)存。譯注但是大對(duì)象內(nèi)存區(qū)本身不是可執(zhí)行的內(nèi)存區(qū)。 Sails.js 是 node 下的一個(gè)優(yōu)秀的 MVC 框架,但是使用 Sails 后,在流量增長(zhǎng)時(shí), node 進(jìn)程有時(shí)突然內(nèi)存暴漲、保持高占用。經(jīng)過(guò)翻閱源碼后,發(fā)現(xiàn)這個(gè)問(wèn)...
摘要:框架核心特性路由定義了路由表用于執(zhí)行不同的請(qǐng)求動(dòng)作。中間件可以設(shè)置中間件來(lái)響應(yīng)請(qǐng)求。注冊(cè)一個(gè)請(qǐng)求路由結(jié)束響應(yīng)開啟監(jiān)聽端口執(zhí)行上面代碼是一種實(shí)用工具,將為您的源的任何變化并自動(dòng)重啟服務(wù)器監(jiān)控。 Express 簡(jiǎn)介 Express 是一個(gè)簡(jiǎn)潔而靈活的 node.js Web應(yīng)用框架, 提供了一系列強(qiáng)大特性幫助你創(chuàng)建各種 Web 應(yīng)用,和豐富的 HTTP 工具。使用 Express 可以快...
摘要:入口文件在文件夾下的,其向外界暴露了一些方法。方法也是從中繼承的。入口文件很清晰,主要是完成方法的暴露以及的一些初始化操作。下一篇寫寫路由的實(shí)現(xiàn)。 還沒用express寫過(guò)server,先把部分源碼擼了一遍,各位大神求輕拍。 express入口文件在lib文件夾下的express.js,其向外界暴露了一些方法。 最主要的(express.js 第36-47行): function cr...
摘要:載入了框架,我們來(lái)看源代碼中的。函數(shù)函數(shù)代碼如下代碼的開始定義了一個(gè)函數(shù),函數(shù)有形參,,為回調(diào)函數(shù)。相應(yīng)的,等同于繼承,從而讓有了事件處理的能力。 此為裁剪過(guò)的筆記版本。 原文在此:https://segmentfault.com/a/11...原文在此: https://cnodejs.org/topic/574... 感謝@YiQi ,@leijianning 帶來(lái)的好文章。我稍作...
摘要:每個(gè)請(qǐng)求都會(huì)對(duì)應(yīng)一個(gè)響應(yīng)。一個(gè)響應(yīng)主要包括狀態(tài)行響應(yīng)頭消息體,將常用的數(shù)據(jù)封裝為類,在上面的代碼中就是該類的一個(gè)對(duì)象。執(zhí)行測(cè)試用例,報(bào)錯(cuò),提示不存在。目前在中,一個(gè)路由是由三個(gè)部分構(gòu)成路徑方法和處理函數(shù)。 1. 簡(jiǎn)介 這篇文章主要的目的是分析理解express的源碼,網(wǎng)絡(luò)上關(guān)于源碼的分析已經(jīng)數(shù)不勝數(shù),這篇文章準(zhǔn)備另辟蹊徑,仿制一個(gè)express的輪子,通過(guò)測(cè)試驅(qū)動(dòng)的開發(fā)方式不斷迭代,正...
閱讀 788·2023-04-25 19:28
閱讀 1446·2021-09-10 10:51
閱讀 2456·2019-08-30 15:55
閱讀 3450·2019-08-26 13:55
閱讀 3051·2019-08-26 13:24
閱讀 3370·2019-08-26 11:46
閱讀 2796·2019-08-23 17:10
閱讀 1469·2019-08-23 16:57