基於 Node.js 平臺,快速、開放、極簡的 web 開發框架
//應用生成器工具 npm install express-generator -g //建立express應用包 express app //安裝依賴 npm install
成功生成後,會產生如下的目錄和文件:node
|---bin |---node_module |---public |---routes |---view |---app.js |---package.json
接下來咱們經過:web
npm start
啓動程序後,訪問127.0.0.1:3000,就能訪問到express的頁面了。express
接下來經過研究源碼,來探討express路由原理的實現。npm
咱們經過查看app.js和index.js文件:json
app.jsapp
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的路由大概實現 定義一份路由規則文件,再經過app.use()或者app[METHOD]來創建路由規則訪問聯繫,雖然二者的結果同樣,可是存在本質上的區別。工具
下圖是主要涉及的幾個文件:ui
接下來咱們經過源碼首先看看app.use()具體是一個什麼樣實現思路。this
咱們打開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實例對象,最終調用router.use()方法。
接着,咱們到lib/router/index.js 看router.use方法的實現:
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 || '<anonymous>') 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一些數據信息:
相同點:
path都是存放掛載路徑,options.end用來判斷是不是路由中間件。
不一樣點:
Router和Route的區別是一個是添非路由中間件,另外一個是添加路由中間件。
他們的layer.route指向也不同,一個指向undefined,另外一個沒有route屬性。
文章進行到一半,咱們小總結一下,app.use()方法是用來添加非路由中間件的,最終是調用router實例方法,會實例劃一個Layer類對象用於存放數據,而且把layer對象push進router.stack裏,全局只有一個router。
咱們經過源碼去探討路由中間件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方法只有一個參數時,是獲取app的本地變量,後面仍是實例化router對象,而且用router上的route方法放回的對象去調用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的沒有layer.route且layer.method存放http方法。
到這裏,咱們大概能夠總結下路由中間件和非路由中間件的聯繫,以下圖:
app初始化時,會push兩個方法(init,query)進router.stack裏。咱們能夠經過app.use往app添加非路由中間件,也能夠經過app[METHOD]添加路由中間件,一樣是push layer實例對象,但route是指向Route實例化的對象。
完整的Router邏輯過程,如圖: