function next(err) {
....//省略了一些代码
// find next matching layer
var layer;
var match;
var route;
while (match !== true && idx < stack.length) {
layer = stack[idx++];
match = matchLayer(layer, path);
route = layer.route;
if (typeof match !== 'boolean') {
// hold on to layerError
layerError = layerError || match;
}
if (match !== true) {
continue;
}
if (!route) {
// process non-route handlers normally
continue;
}
....//省略一些源码
}
// no match
if (match !== true) {
return done(layerError);
}
// store route for dispatch on change
if (route) {
req.route = route;
}
.....
// this should be done for the layer
self.process_params(layer, paramcalled, req, res, function (err) {
if (err) {
return next(layerError || err);
}
if (route) {
return layer.handle_request(req, res, next);
}
trim_prefix(layer, layerError, layerPath, path);
});
}
最近在研究
experss
,在造过一个简单的轮子之后,想通过研究一下它的源码来了解一下内部的实现原理,如有不对的地方希望得到大家的指正。研究的
express
版本为4.15.0
,应该算是比较新的版本了。文件结构及内容
上图中的目录结构比较清晰,
router
目录中主要是router
路由的功能,middleware
目录是中间件的一些功能。比较重要的是express.js
和application.js
还有router
文件夹中的文件。express.js
中很简单,只是暴露了一个工厂函数createApplication
,这个函数虽然简洁,但是却完成了生成一个完成app
的整个过程,具体代码如下:在
application.js
中,可以看到app.listen
的实现:由此就可以知道使用
express
实例化一个项目,其实和使用原生node
创建一个应用的步骤差不多,只是express
进行了一层封装而已。application.js
这个文件主要是暴露了express
内部的一些api
,比如app.render(),app.param(),app.set()
等。路由
express
的router
中总共包含了三个文件,index.js
,route.js
和layer.js
,并且分别定义了router,route,layer
三个构造函数,其中router
和route
这两个对象都包含stack
这个成员属性,也就是中间件数组。并且router
和route
里面的stack
也不一样,如下图:如上图可见,
router
中的stack
数组是由route
对象组成的,而route
中的stack
则是由layer
数组组成的。三者关系可以描述成下图:route
对象代表的是路由对象,每一条路由都会实例化一个route
对象,而router
则是一个路由集合,在上一篇中曾提到router
是一个“微型应用程序”,这说明router
和route
之间的关系是一个包含与被包含的关系,前者的功能更强大,后者只是处理单条路由的一些功能。 路由的整个功能和逻辑顺序大概如下: 在触发一个路由的时候,比如访问/login
时,会执行router
的成员函数handle()
,在这个函数中会将传进来的req
的url
做一些处理,比如获取url
里面的参数,并会遍历stack
数组中的每一个layer
,遍历主要是通过next()
实现的,这个next()
主要是非路由中间件的next()
函数,具体代码如下:遍历过程中会执行一个
match()
,如果匹配成功(断点发现当stack.name=router
的时候会匹配成功),则会去执行route
中的dispatch
方法,则获取当前route
的stack
数组并通过next()
再进行遍历一次。当再次匹配成功之后,就会去调用layer
对象的handle_request()
方法。也就是调用中间件函数,这个函数的代码如下:上面也是执行中间件的主要部分,首先是判断路由函数中传进来的回调函数的参数个数,若大于3,则属于路由中间件,则首先处理
next()
,这里的next()
函数代码如下(定义在route.js
文件中:这个
next()
主要是处理路由中间件的,不断遍历,使得中间件一个个执行。对于路由中间件和非路由中间件,两者也是有区别的,路由中间件的定义是在router/index.js
中的route
原型方法中,具体代码如下:从上面代码可以看出,路由中间件中的
route
不为空值,且为一个route
对象,并且该对象中有methods
的一个成员属性,用来定义一些路由方法。再来看看非路由中间件的定义方法,放在了router/index.js
中的use
原型方法中,具体代码如下:从上面源码可以看出,非路由中间件中的
route
属性值为undefined
,当使用app.use()
时其实也就是触发了router.use()
方法。动态添加
method
源码中并没有直接遍历路由中的每一种方法,比如
get,post,put
,而是动态添加,不论是最后导出的app
示例,还是route
中对方法的处理,都是使用的这种方式,具体代码如下:在
route
对象中,同样定义了这样一个处理method
的操作:这种方法比较灵活,不需要将所有的
method
遍历一遍。总结
express
的源码还是有点复杂的,新手初涉,可能有一些地方理解得不是很到位的,欢迎一起交流