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