zoukankan      html  css  js  c++  java
  • .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方法,下一节看看具体请求方法的源码流向。

  • 相关阅读:
    hadoop再次集群搭建(3)-如何选择相应的hadoop版本
    48. Rotate Image
    352. Data Stream as Disjoint Interval
    163. Missing Ranges
    228. Summary Ranges
    147. Insertion Sort List
    324. Wiggle Sort II
    215. Kth Largest Element in an Array
    快速排序
    280. Wiggle Sort
  • 原文地址:https://www.cnblogs.com/QH-Jimmy/p/8868633.html
Copyright © 2011-2022 走看看