.6-淺析express源碼之Router模塊(2)-router.use


  這一節繼續深入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方法,下一節看看具體請求方法的源碼流向。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM