最后我们来看下route模块,之前connect.js的route模块写的太TM坑爹了,相当混乱,作者显然也是发现了这个问题,重新在expressjs中改写了route模块。
新的route模块包含4个文件,我们一个个来看,
1、index.js:主要功能模块
2、collection.js:收集remove的route
3、methods.js:http请求方法的数组
4、route.js:匹配和正则化path
1、route.js
包含2个主要方法:1
2
3Route.prototype.match = function(path){
return this.regexp.exec(path);
};
匹配的方法,传入path参数,然后利用原先定义的path进行匹配,返回Regexp.exe()结果的数组。也就是说如果我们定义了多个路由规则,则会实例化多个Route类。1
function normalize(path, keys, sensitive, strict) {...}
这个方法名叫:正常化,其实更贴切的应该是叫 正则化,他会根据传入的path路径还有参数等字符串,拼装成一个正则表达式,供以后math方法使用。具体实现太TM复杂,我不大在行正则就不分析拉,免得献丑。
2、collection.js
1 | function Collection(router) { |
定义Collection类的属性router1
Collection.prototype.__proto__ = Array.prototype;
将Collection的原型链的原型父链指向Array的原型链,这样Collection就继承了Array.prototype的方法,也就是数组的一些方法。同时他还是Function的一个实例,并且他还有一个属性router。1
2
3
4
5
6
7
8
9
10
11
12Collection.prototype.remove = function(){
var router = this.router
, len = this.length
, ret = new Collection(this.router);
for (var i = 0; i < len; ++i) {
if (router.remove(this[i])) {
ret.push(this[i]);
}
}
return ret;
};
这个代码主要是调用Collection的router属性对象的remove方法删除原本Collection实例的内容复制到另外一个新的Collection示例ret数组中。
看到这里我们发现Collection实例化的数组既有数组的方法,也有属性,这里只要理解javascript一切皆对象。
比如:1
2
3
4
5
6
7var a=[1,2,3,4];
a.msg = '我是数组';
a.alt = function(){
alert(this.msg);
}
a.alt(); //弹出我是数组
alert(a.length); //弹出4
3、index.js
最重要的一般都放在最后,index.js就是提供route类的核心函数1
2
3
4
5
6
7
8
9
10
11function Router(app) {
var self = this;
this.app = app;
this.routes = new Collection;
this.map = {};
this.params = {};
this._params = [];
this.middleware = function(req, res, next){
self._dispatch(req, res, next);
};
}
传入了一个app参数,这里作者用 this.routes = new Collection; 就是实例化Colection类,比较装B的写法,表示不传参数的new func()。
还定义了一个middleware函数。1
Router.prototype.param = function(name, fn){...}
注册一个回调函数,是配合 app.param()方法一起使用的。
我们回过头来先看看app.param:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23app.param = function(name, fn){
var self = this
, fns = [].slice.call(arguments, 1);
// array
if (Array.isArray(name)) {
name.forEach(function(name){
fns.forEach(function(fn){
self.param(name, fn);
});
});
// param logic
} else if ('function' == typeof name) {
this._router.param(name);
// single
} else {
if (':' == name[0]) name = name.substr(1);
fns.forEach(function(fn){
self._router.param(name, fn);
});
}
return this;
};
app.param期望接收2个参数,当然第一个参数是数组时,就将数组中每一项name和回调fn做递归。
当第一个参数是函数时,则expressjs认为是注册一种匹配的函数,转给route.param方法。
最后进行判断name的合法性和fn是否是数组,将1个name和1个fn作为参数转给route.param方法。
然后我们看下route.param方法:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27Router.prototype.param = function(name, fn){
// param logic
if ('function' == typeof name) {
this._params.push(name);
return;
}
// apply param functions
var params = this._params
, len = params.length
, ret;
for (var i = 0; i < len; ++i) {
if (ret = params[i](name, fn)) {
fn = ret;
}
}
// ensure we end up with a
// middleware function
if ('function' != typeof fn) {
throw new Error('invalid param() call for ' + name + ', got ' + fn);
}
(this.params[name] = this.params[name] || []).push(fn);
return this;
};
如果第一个参数是function,则向route._params数组插入这个function,然后直接return;
执行route._params中的函数将返回函数赋值个头ret
接着合法性判断,然后将name作为key,fn作为值放置到route.param数组中,以供后面获取这个参数时执行回调。1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18Router.prototype.find = function(fn){
var len = methods.length
, ret = new Collection(this)
, method
, routes
, route;
for (var i = 0; i < len; ++i) {
method = methods[i];
routes = this.map[method];
if (!routes) continue;
for (var j = 0, jlen = routes.length; j < jlen; ++j) {
route = routes[j];
if (fn(route)) ret.push(route);
}
}
return ret;
};
根据methods数组进行循环,methods数组就是methods.js文件,如果map对象中不存有这个方法,也就是用户没有注册过这个方法,则继续查找。
当找到用户注册的方法,然后根据fn回调进行操作,如果返回true,则将route对象存入collection实例的ret中。
最后返回collection实例。
好我们跳着看,那个存入map对象中的以请求method为key的对象是什么呢?1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18Router.prototype._route = function(method, path, callbacks){
var app = this.app
, callbacks = utils.flatten([].slice.call(arguments, 2));
// ensure path was given
if (!path) throw new Error('app.' + method + '() requires a path');
// create the route
var route = new Route(method, path, callbacks, {
sensitive: app.enabled('case sensitive routing')
, strict: app.enabled('strict routing')
});
// add it
(this.map[method] = this.map[method] || []).push(route);
this.routes.push(route);
return this;
};
它就是这个,用_route方法注册map[method]中的route对象。sensitive,strict 这2个参数是用来生成正则使用,第一个参数是是否严格匹配,第二个参数是是否区分大小写。
1 | Router.prototype._options = function(req, res){ |
这个options函数是用来接收option请求的,将返回服务器所支持的所有请求格式,例如”get,post等等”
两个重头戏:1
_dispatch和_match,
调度员和比对两个方法我下一节再详细说。