探讨Express Router Route
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 pathif (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 routerthis.lazyrouter();var router = this._router;fns.forEach(function(fn) {// non-express appif (!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 resrouter.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 appfn.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 pathif (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 middlewaredebug('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一些數(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é)
stack里的路由中間件,通過Layer.route指向Route,與Route stack相關(guān)聯(lián)起來
總結(jié)
以上是生活随笔為你收集整理的探讨Express Router Route的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 怎样界定问题
- 下一篇: Oracle创建表,并添加默认值和备注