摘要:就是每一個教程里面開始教學的事例,啟動服務器的回調函數。,從入口開始分析源碼首先是把模塊的屬性全部進里面去,在把事件的屬性全部進里面去,這是為了給增加事件功能。
express4.X源碼解讀第一天
express4.X 跟3.X 有很大區別,4.X 去除了connect的依賴,3.X基于connect的中間件基本全部不能用,如果還有可以使用的,也是4.X重寫的。所以要想繼續使用這些熟悉的中間件,就要手動安裝依賴包,或者用一些其他的中間件。
下面開始源碼解讀 1. express是什么typeof express === "function" //true
可以知道express是個函數,這個函數是程序啟動就會運行起來
function createApplication() { var app = function(req, res, next) { app.handle(req, res, next); }; mixin(app, proto); mixin(app, EventEmitter.prototype); app.request = { __proto__: req, app: app }; app.response = { __proto__: res, app: app }; app.init(); return app; }
上面這個函數就是express,有沒有看到很熟悉的東西,看到app沒,還在哪里看到過這個熟悉的東西...
對了,沒錯。就是每一個nodejs教程里面開始nodejs教學的事例,nodejs啟動服務器:http.createSever 的回調函數。app是express貫穿整個流程的函數。其實整個express 執行過程就是往req,res這兩個對象不停的修改屬性,添加屬性。直到完成請求。中間件也就是通過app做為回調,進而修改req,res。從而實現可插拔的效果。
var app = express();
這就是為什么引入express,都要開始執行一下這個函數。
2. 程序是如何啟動的express做為一個web框架,首先要有啟動一個服務器的,我們看下服務器是在哪里啟動的
var server = app.listen(app.get("port"), function() { debug("Express server listening on port " + server.address().port); });
express用了一個我不太喜歡用的寫法,他把所有的方法直接放到app這個函數上去了,大家都知道函數在js中就是對象,除了本身是可以執行以外,和對象是沒有什么區別的。不過這就無形之中增加了閱讀代碼的難度,而且很容易混淆,因為app既做為一個中間件,還要做為一個公共方法的載體。
好了,講到啟動服務器,app是沒有啟動服務器的能力的,這個能力是在application 這個文件中被mix進去的,其實就是mix一個http.createServer方法,但是這里還是要看一下代碼。
app.listen = function(){ var server = http.createServer(this); return server.listen.apply(server, arguments); };
看到this沒有啊,這個this很重要哈,this == app 。app做為回調已經傳進來了,神奇的中間件在這里開始了旅程。
3,從入口開始分析源碼function createApplication() { var app = function(req, res, next) { app.handle(req, res, next); }; mixin(app, proto); mixin(app, EventEmitter.prototype); app.request = { __proto__: req, app: app }; app.response = { __proto__: res, app: app }; app.init(); return app; }
好,下面到了application模塊的init方法里面去了
app.init = function(){ this.cache = {}; this.settings = {}; this.engines = {}; this.defaultConfiguration(); };
增加了cache setting engines 三個對象,現在看不出來作用,具體執行過程到defaultConfiguration里面看看
this.enable("x-powered-by")
看到了enable,然后進去看enable其實就set,只不過第二個參數是boolean。set是什么呢?還記得我們沒有了解功能的三個對象之一的setting,這個set就是往setting對象添加一些屬性而已。
好 先看defaultConfiguration
this.enable("x-powered-by")
設置x-powered-by 為true,x-powerd-by是什么意思呢?
有些查詢工具在我們輸入某個站點的URL后就能判斷這個站點的WebServer與程序類型。
就是在http請求的時候,能夠看到x-powered-by:Express,不設置 就看不到服務區類型,這應該是http請求的一部分
this.set("etag", "weak");
這里處理etag的 Express依賴了一個叫etag的包
var env = process.env.NODE_ENV || "development"; this.set("env", env); this.set("query parser", "extended"); this.set("subdomain offset", 2); this.set("trust proxy", false);
這里繼續設置屬性。
// inherit protos this.on("mount", function(parent){ this.request.__proto__ = parent.request; this.response.__proto__ = parent.response; this.engines.__proto__ = parent.engines; this.settings.__proto__ = parent.settings; }); // setup locals this.locals = Object.create(null); // top-most app is mounted at / this.mountpath = "/"; // default locals this.locals.settings = this.settings; // default configuration this.set("view", View); this.set("views", resolve("views")); this.set("jsonp callback name", "callback"); if (env === "production") { this.enable("view cache"); } Object.defineProperty(this, "router", { get: function() { throw new Error(""app.router" is deprecated! Please see the 3.x to 4.x migration guide for details on how to update your app."); } });
這里的mount,我之前不知道什么意思,后來看其他應用才知道,這是用來掛載其他應用的,比如我有幾個應用,可以起幾個業務服務,用一個中央服務監聽端口,然后掛載其他幾個應用模塊
下面研究一下app.use這個方法研究發現這個時候express的初始化流程已經走完了,以前看過3.X的源碼,貌似不是這樣子的,但是仔細觀察,確確實實到這里是結束了。剩余的方法都是怎么處理的呢?在細細往下看吧
add middleware to the app router
這是源碼里面的解釋,向路由添加中間件,前面說過中間件和路由沒有本質區別,是一樣的東西。
app.use = function use(fn) { var offset = 0; var path = "/"; var self = this; // 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 = self; // 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) { req.__proto__ = orig.request; res.__proto__ = orig.response; next(err); }); }); // mounted an app fn.emit("mount", self); }); return this; };
于是我們看到lazyrouter這么個東西,這個函數里面new 了一個Router對象,所以這一張暫時略過了 我們要去route里面看看了
昨天看源碼遇到了麻煩,發現很多代碼還不是那么容易看懂,有些迷糊,然后犯了一些錯誤,打了很多斷點終于弄清楚了
想要明白express的處理流程,必須先要弄清楚app.use和 app.handle這兩個方法,這兩個方法很重要。
前面我們已經知道app本身是做為回調參數傳進http.createServer里面的,應用所有的路由都會掉進這個函數里面去,經過一個一個中間件進行處理。本身想想不是很復雜,但看起代碼來還是很蛋疼的
首先req,res被封裝了很多方法進去,但是這個方法是在什么地方mix進去的呢。在這里我就犯了個錯誤,錯誤的認為會在use的時候就會有這個方法,所以我在use函數里面找啊找,打了很多個斷點,始終沒有找到哪里執行了這個操作。
但實際上,use始終沒有做這個操作,use的作用就是route里面把這個回調push進route實例的stack里面,看代碼
if (!fn || !fn.handle || !fn.set) { return router.use(path, fn); }
app的use執行了 Route實例的use。繼續看Route的use
var layer = new Layer(path, { sensitive: self.caseSensitive, strict: false, end: false }, fn); layer.route = undefined; self.stack.push(layer);
去看會發現route的use和app的use會有些重復的代碼,不同的地方就在于Route的use會創建一個layer。這個layer就是個實例,就是每個回調函數的實例。這個實例包括全局配置的一些屬性,比如嚴格匹配,大小寫。還有就是把當前use的路由url和回調存儲起來了,全部push進stack里面去。
看下route的實例化過程,會發現express默認放置了兩個中間件進去。代碼如下
app.lazyrouter = function() { if (!this._router) { this._router = new Router({ caseSensitive: this.enabled("case sensitive routing"), strict: this.enabled("strict routing") }); this._router.use(query(this.get("query parser fn"))); this._router.use(middleware.init(this)); } };
所以app默認就會有兩個中間件,query和 middleware。程序執行到這里已經執行結束了。
那又有問題了,request,response這兩個對象的很多擴展方法,從何而來。
下面就來看看吧
打開middleware/init
exports.init = function(app){ return function expressInit(req, res, next){ if (app.enabled("x-powered-by")) res.setHeader("X-Powered-By", "Express"); req.res = res; res.req = req; req.next = next; req.__proto__ = app.request; res.__proto__ = app.response; res.locals = res.locals || Object.create(null); next(); }; };
這里就看到了 request,response是在這里被放置到回調的req,res上去的。由于內置的這兩個中間件是首先添加的,被放置在stack的前兩個,所以每個請求進來首先會進入這兩個中間件里面去,然后帶了很多東西進入其他的中間件去。
還有問題啊,use不是可以增加路由嗎 不是可以控制哪一些中間件走哪一些路由嘛,那是怎么控制的呢??催@里。。。
proto.match_layer = function match_layer(layer, req, res, done) { var error = null; var path; try { path = parseUrl(req).pathname; if (!layer.match(path)) { path = undefined; } } catch (err) { error = err; } done(error, path); };
這里會把layer里面存儲的route正則拿來和當前路由匹配,成功則進入回調執行,失敗則繼續執行。
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/82627.html
摘要:打包好后本地測試運行是否正常環境搭建這個時候需要利用中的方法如下安裝最新版本中將命令工具分家出來了還需要安裝一個命令工具創建一個工程進入項目主目錄安裝必備包啟動程序把打包后的文件夾放在文件夾里訪問就能看到項目了這樣測試好了后就可以丟后 打包好后本地測試運行是否正常環境搭建: 這個時候需要利用node中的express,方法如下: 安裝express: npm install -g ex...
摘要:前言由于最近公司需要做一個聊天監控的項目,老大讓我把后臺也做了,于是才真正實踐深入的內部。幾番折騰終于把項目搭起來了。發生服務特定錯誤,則前去目錄下的和文件刪掉,以管理員身份運行命令行然后重新啟動服務即可。 前言 由于最近公司需要做一個聊天監控的項目,老大讓我把后臺也做了,于是才真正實踐深入node.js的內部。幾番折騰終于把項目搭起來了。 經濟基礎 node.js (安裝配置傳送門...
摘要:前言由于最近公司需要做一個聊天監控的項目,老大讓我把后臺也做了,于是才真正實踐深入的內部。幾番折騰終于把項目搭起來了。發生服務特定錯誤,則前去目錄下的和文件刪掉,以管理員身份運行命令行然后重新啟動服務即可。 前言 由于最近公司需要做一個聊天監控的項目,老大讓我把后臺也做了,于是才真正實踐深入node.js的內部。幾番折騰終于把項目搭起來了。 經濟基礎 node.js (安裝配置傳送門...
摘要:最近寫復旦二手平臺的時候開始嘗試用一直推崇了很久的組件化。經過一番抉擇之后選擇了的組合。所以在這里分享一下具體的實踐流程。自己有自己獨特的依賴注入以及模塊聲明方式,看起來似乎和是水火不容的,但事實上他們完全可以融合。 最近寫復旦二手平臺的時候開始嘗試用一直推崇了很久的組件化。經過一番抉擇之后選擇了 webpack + angular 的組合。所以在這里分享一下具體的實踐流程。 Web...
摘要:有如下模塊源碼解析源碼解析源碼解析源碼解析源碼解析源碼解析源碼解析源碼解析源碼解析使用和監控和博客從到學習介紹從到學習上搭建環境并構建運行簡單程序入門從到學習配置文件詳解從到學習介紹從到學習如何自 Flink Metrics 有如下模塊: Flink Metrics 源碼解析 —— Flink-metrics-core Flink Metrics 源碼解析 —— Flink-metr...
閱讀 3306·2021-11-23 09:51
閱讀 2925·2021-10-28 09:33
閱讀 890·2021-10-08 10:04
閱讀 3694·2021-09-22 15:13
閱讀 1023·2019-08-30 15:55
閱讀 2912·2019-08-30 15:44
閱讀 571·2019-08-30 13:04
閱讀 2942·2019-08-30 12:56