这一节继续深入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方法,下一节看看具体请求方法的源码流向。

.6-浅析express源码之Router模块(2)-router.use的更多相关文章

  1. .2-浅析express源码之applicaiton模块(1)-咸鱼方法

    上一节讲了express的入口文件,当执行主函数,会调用app.init方法,这个方法就来源于application模块. 这个模块有很多方法,目前仅仅过一下初始化方法: app.init = fun ...

  2. .3-浅析express源码之applicaiton模块(2)-app.render

    这个模块还漏了一个稍微复杂点的API,就是app.render,首先看官网的定义: app.render(view, [locals], callback) view为对应的文件名,locals为一个 ...

  3. .4-浅析express源码之applicaiton模块(3)-compile函数

    基本上application模块的api都看的差不多了,但是在app.set中还有一个遗漏点,如下: app.set = function set(setting, val) { // ...设值 / ...

  4. .5-浅析express源码之Router模块(1)-默认中间件

    模块application已经完结,开始讲Router路由部分. 切入口仍然在application模块中,方法就是那个随处可见的lazyrouter. 基本上除了初始化init方法,其余的app.u ...

  5. express源码分析之Router

    express作为nodejs平台下非常流行的web框架,相信大家都对其已经很熟悉了,对于express的使用这里不再多说,如有需要可以移步到www.expressjs.com自行查看express的 ...

  6. nginx源码分析之模块初始化

    在nginx启动过程中,模块的初始化是整个启动过程中的重要部分,而且了解了模块初始化的过程对应后面具体分析各个模块会有事半功倍的效果.在我看来,分析源码来了解模块的初始化是最直接不过的了,所以下面主要 ...

  7. 读Zepto源码之Event模块

    Event 模块是 Zepto 必备的模块之一,由于对 Event Api 不太熟,Event 对象也比较复杂,所以乍一看 Event 模块的源码,有点懵,细看下去,其实也不太复杂. 读Zepto源码 ...

  8. 读Zepto源码之Callbacks模块

    Callbacks 模块并不是必备的模块,其作用是管理回调函数,为 Defferred 模块提供支持,Defferred 模块又为 Ajax 模块的 promise 风格提供支持,接下来很快就会分析到 ...

  9. 读Zepto源码之Deferred模块

    Deferred 模块也不是必备的模块,但是 ajax 模块中,要用到 promise 风格,必需引入 Deferred 模块.Deferred 也用到了上一篇文章<读Zepto源码之Callb ...

  10. 读Zepto源码之Ajax模块

    Ajax 模块也是经常会用到的模块,Ajax 模块中包含了 jsonp 的现实,和 XMLHttpRequest 的封装. 读 Zepto 源码系列文章已经放到了github上,欢迎star: rea ...

随机推荐

  1. [logic]逻辑整理

    圈子详情页面:    1.加入圈子(*)        1.已登录,直接添加        2.未登录,登陆框    2.发表新帖(*)        1.已登录,直接跳转        2.未登录, ...

  2. java 堆排序的实现

    堆就是一个完全二叉树,堆要求是指 该节点大于它的两个子节点.而两个字节点大小不一定. 堆排序的最坏时间复杂度为nlog(n),平均也为nlog(n),占用空间为o(1),是一种比较排序算法. 堆排序也 ...

  3. ASP.NET WebAPI (反)序列化用[SerializableAttribute]修饰的类的一个坑

    发现问题 在 ASP.NET WebAPI 项目中,有这样的 ViewModel 类: [Serializable] class Product { public int Id { get; set; ...

  4. MVC简介与三层架构

    感谢博客园团队日夜辛苦的付出 感谢阅读我文章的每位读者 1.MVC简介 MVC最早于1978年提出,是软件工程中的一种软件架构模式,这时距离微软在1985年推出Window1.0还有7年之久,当时的M ...

  5. Resolving SharePoint Application Authentication Error: Login Failed

    Check event viewer log Click Start, click Run, type eventvwr, and then click OK. Click on Security u ...

  6. [leetcode.com]算法题目 - Symmetric Tree

    Given a binary tree, check whether it is a mirror of itself (ie, symmetric around its center). For e ...

  7. postgresql-shared_buffers调整

    shared_buffers大小调整: http://www.rummandba.com/2011/02/sizing-sharedbuffer-of-postgresql.html SELECT  ...

  8. 机器学习与Tensorflow(1)——机器学习基本概念、tensorflow实现简单线性回归

    一.机器学习基本概念 1.训练集和测试集 训练集(training set/data)/训练样例(training examples): 用来进行训练,也就是产生模型或者算法的数据集 测试集(test ...

  9. springbootf访问静态文件资源

    springboot目录结构: 网友说在springboot的配置文件中加 现在访问static目录下的jquery文件 用jquery在页面做一个弹窗 启动服务看页面效果 页面没有出现弹窗 ,连jq ...

  10. odoo API装饰器one、model、multi的区别

    1.one装饰器详解 odoo新API中定义方式:    date=fields.Date(string="date",compute="_get_date") ...