这一节继续深入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. apollo broker 启动流程

    1.下载Apollo服务器,下载后解压,然后运行apache-apollo-1.6\bin\apollo.cmd,输入create mybroker(名字任意取,这里是根据官网介绍的来取的)创建服务器 ...

  2. caffe 教程

    Caffe是一个清晰而高效的深度学习框架,本文详细介绍了caffe的优势.架构,网络定义.各层定义,Caffe的安装与配置,解读了Caffe实现的图像分类模型AlexNet,并演示了CIFAR-10在 ...

  3. 使用PerfView监测.NET程序性能(二):Perfview的使用

    在上一篇博客中,我们了解了对Windows及应用程序进行性能分析的基础:Event Trace for Windows (ETW).现在来看看基于ETW的性能分析工具——Perfview.exe Pe ...

  4. node.js second day

    create global link 使用全局模式安装的包不能直接通过require使用,但是nmp提供了一个 nmp link ,这个可以打破限制 $ nmp link [express] ./no ...

  5. python网络爬虫抓取网站图片

    本文介绍两种爬取方式: 1.正则表达式 2.bs4解析Html 以下为正则表达式爬虫,面向对象封装后的代码如下: import urllib.request # 用于下载图片 import os im ...

  6. 如何获取SQL中Print语句输出内容

    SqlConnection cn = new SqlConnection("server=my\\my2005;database=rdwhdata2005;user id=zjh;passw ...

  7. Java几种单例模式的实现与利弊

    饿汉式 提前new出来实例了,并不是在第一次调用get方法时才实例化,没有进行延迟加载 public class Singleton1 { private static Singleton1 inst ...

  8. python项目实现配置统一管理的方法

    一个比较大的项目总是会涉及到很多的参数,最好的方法就是在一个地方统一管理这些参数.最近看了不少的python项目,总结了两种很有意思的配置管理方法. 第一种 基于easydict实现的配置管理 首先需 ...

  9. javaweb项目中的过滤器的使用

    翻阅博客园的的时候,看到两篇关于javaweb过滤器的帖子写的很好,这里备忘一下: 过滤器基础:http://www.cnblogs.com/xdp-gacl/p/3948353.html 获取器案例 ...

  10. CentOS的ssh sftp配置及权限设置[转载-验证可用]

    从技术角度来分析,几个要求:1.从安全方面看,sftp会更安全一点2.线上服务器提供在线服务,对用户需要控制,只能让用户在自己的home目录下活动3.用户只能使用sftp,不能ssh到机器进行操作 提 ...