這一節繼續深入Router模塊,首先從最常用的use開始。 router.use 方法源碼如下: 前半部分十分熟悉,根本就是app.use的翻版。 當然,最後遍歷中間件函數處理的時候就不一樣了,引入了新的本地模塊Layer。 Layer 不太理解這個層的意義,無論是app.use還是router.u ...
這一節繼續深入Router模塊,首先從最常用的use開始。
router.use
方法源碼如下:
proto.use = function use(fn) { var offset = 0; var path = '/'; if (typeof fn !== 'function') { var arg = fn; while (Array.isArray(arg) && arg.length !== 0) arg = arg[0]; 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 a middleware function') for (var i = 0; i < callbacks.length; i++) { var fn = callbacks[i]; if (typeof fn !== 'function') throw new TypeError('Router.use() requires a middleware function but got a ' + gettype(fn)) debug('use %o %s', path, fn.name || '<anonymous>'); // 內部模塊layer! var layer = new Layer(path, { sensitive: this.caseSensitive, strict: false, end: false }, fn); // 通過use方法生成的layer沒有route值 layer.route = undefined; // 初始化時定義的數組 this.stack.push(layer); } return this; };
前半部分十分熟悉,根本就是app.use的翻版。
當然,最後遍歷中間件函數處理的時候就不一樣了,引入了新的本地模塊Layer。
Layer
不太理解這個層的意義,無論是app.use還是router.use,每一個中間件都會生成一個layer對象,然後push進router上的stack數組。
那麼多路徑呢,是否會生成多個layer?答案是否。
看一眼layer的構造函數:
function Layer(path, options, fn) { if (!(this instanceof Layer)) { return new Layer(path, options, fn); } debug('new %o', path) var opts = options || {}; /** * layer.handle => 中間件函數 * layer.name => 函數名 * layer.regexp => 路徑的正則 */ this.handle = fn; this.name = fn.name || '<anonymous>'; this.params = undefined; this.path = undefined; this.regexp = pathRegexp(path, this.keys = [], opts); // 快速匹配標記 this.regexp.fast_star = path === '*' this.regexp.fast_slash = path === '/' && opts.end === false }
其中比較關鍵一步是根據傳進來的path生成一個正則,pathRegexp是一個工具模塊,無論傳進去的是字元串、數組、正則都能返回一個正則匹配需要的值。
簡略的看一下工具核心源碼:
function pathtoRegexp(path, keys, options) { // ... // 字元串 path = ('^' + path + (strict ? '' : path[path.length - 1] === '/' ? '?' : '/?')); // ...後面有很多replace // 數組 if (Array.isArray(path)) { path = path.map(function(value) { return pathtoRegexp(value, keys, options).source; }); // 使用|分割多個規則來進行多重匹配 return new RegExp('(?:' + path.join('|') + ')', flags); } // 正則 比較簡單的 // var MATCHING_GROUP_REGEXP = /\((?!\?)/g; if (path instanceof RegExp) { // 匹配組 while (m = MATCHING_GROUP_REGEXP.exec(path.source)) { keys.push({ name: name++, optional: false, offset: m.index }); } return path; } }
字元串模式非常複雜,因為允許類正則寫法的字元串,解析會變得十分複雜,後面有很多很多的replace,這裡給一個開頭,比較簡單過把癮。
最後返回一個匹配路徑的正則表達式,然後在該對象上加兩個標記,比如說如果一個Layer的正則對象有全局路由標記,則根本不用正則校驗,直接可以調用中間件。
返回Layer對象後,該對象會被push進router的stack數組。
這節就簡單過一下Router模塊的use方法,下一節看看具體請求方法的源碼流向。