.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方法,下一节看看具体请求方法的源码流向。
.6-浅析express源码之Router模块(2)-router.use的更多相关文章
- .2-浅析express源码之applicaiton模块(1)-咸鱼方法
上一节讲了express的入口文件,当执行主函数,会调用app.init方法,这个方法就来源于application模块. 这个模块有很多方法,目前仅仅过一下初始化方法: app.init = fun ...
- .3-浅析express源码之applicaiton模块(2)-app.render
这个模块还漏了一个稍微复杂点的API,就是app.render,首先看官网的定义: app.render(view, [locals], callback) view为对应的文件名,locals为一个 ...
- .4-浅析express源码之applicaiton模块(3)-compile函数
基本上application模块的api都看的差不多了,但是在app.set中还有一个遗漏点,如下: app.set = function set(setting, val) { // ...设值 / ...
- .5-浅析express源码之Router模块(1)-默认中间件
模块application已经完结,开始讲Router路由部分. 切入口仍然在application模块中,方法就是那个随处可见的lazyrouter. 基本上除了初始化init方法,其余的app.u ...
- express源码分析之Router
express作为nodejs平台下非常流行的web框架,相信大家都对其已经很熟悉了,对于express的使用这里不再多说,如有需要可以移步到www.expressjs.com自行查看express的 ...
- nginx源码分析之模块初始化
在nginx启动过程中,模块的初始化是整个启动过程中的重要部分,而且了解了模块初始化的过程对应后面具体分析各个模块会有事半功倍的效果.在我看来,分析源码来了解模块的初始化是最直接不过的了,所以下面主要 ...
- 读Zepto源码之Event模块
Event 模块是 Zepto 必备的模块之一,由于对 Event Api 不太熟,Event 对象也比较复杂,所以乍一看 Event 模块的源码,有点懵,细看下去,其实也不太复杂. 读Zepto源码 ...
- 读Zepto源码之Callbacks模块
Callbacks 模块并不是必备的模块,其作用是管理回调函数,为 Defferred 模块提供支持,Defferred 模块又为 Ajax 模块的 promise 风格提供支持,接下来很快就会分析到 ...
- 读Zepto源码之Deferred模块
Deferred 模块也不是必备的模块,但是 ajax 模块中,要用到 promise 风格,必需引入 Deferred 模块.Deferred 也用到了上一篇文章<读Zepto源码之Callb ...
- 读Zepto源码之Ajax模块
Ajax 模块也是经常会用到的模块,Ajax 模块中包含了 jsonp 的现实,和 XMLHttpRequest 的封装. 读 Zepto 源码系列文章已经放到了github上,欢迎star: rea ...
随机推荐
- uniGUI 通过SessionList操作另外的登录用户
参照bbs,写了这个方法,检查是否有同名用户已经登录:procedure TUniMainModule.CheckSameUser(aUserLoginCode: string);var ASess ...
- 深度学习主机环境配置: Ubuntu16.04+GeForce GTX 1080+TensorFlow
接上文<深度学习主机环境配置: Ubuntu16.04+Nvidia GTX 1080+CUDA8.0>,我们继续来安装 TensorFlow,使其支持GeForce GTX 1080显卡 ...
- 二、基于kubernetes构建Docker集群环境实战
kubernetes是google公司基于docker所做的一个分布式集群,有以下主件组成 etcd: 高可用存储共享配置和服务发现,作为与minion机器上的flannel配套使用,作用是使每台 m ...
- bootstrap基础学习小记(一)简介模板、全局样式
2011年,twitter的“一小撮”工程师为了提高他们内部的分析和管理能力,用业余时间为他们的产品构建了一套易用.优雅.灵活.可扩展的前端工具集--BootStrap.Bootstrap由MARK ...
- Flask系列07--Flask中的CBV, 蓝图的CBV
一.CBV使用 class base view 和django中类似 class Login(views.MethodView): # methods=["POST"," ...
- 使用Squid部署代理服务
Squid是Linux系统中最为流行的一款高性能代理服务软件,通常用作Web网站的前置缓存服务,能够代替用户向网站服务器请求页面数据并进行缓存.简单来说,Squid服务程序会按照收到的用户请求向网站源 ...
- django -- 美多订单分表
订单分表: 随着公司业务增长,如果每天1000多万笔订单的话,3个月将有约10亿的订单量,之前数据库采用单表的形式已经不满足于业务需求,数据库改造迫在眉睫. 解决思路: 按月分表,将原订单表拆分为 o ...
- MyBatis框架介绍及其实操
一.基本概念和介绍 数据持久化的概念 数据持久化是将内存中的数据模型转换为存储模型,以及将存储模型转换为内存中的数据模型的统称.例如,文件的存储.数据的读取等都是数据持久化操作.数据模型可以是任何数据 ...
- 10-05 Java 内部类概述和讲解
内部类的概述 /* 内部类概述: 把类定义在其他类的内部,这个类就被称为内部类. 举例:在类A中定义了一个类B,类B就是内部类. 内部的访问特点: A:内部类可以直接访问外部类的成员,包括私有. B: ...
- 13、最新安卓Xamarin绑定相关填坑之旅
今天群里面有兄弟伙说第三方库用不起.说实话在我觉得第三方库能成功的几率大于90% 除了极少数恶心的库以外. 绝大部分第三方库都还是可以绑定好的 https://github.com/youzan/Yo ...