使用express,对route部分比较好奇,花了两天时间看了下源码,今天分享出来,但愿能够给对express route实现一样好奇的人带来一些帮助.express
app.use((req,res,next)=>{
console.log('use 中间件');
next();
})
app.get('/' , (req,res,next)=>{
res.end('main');
})
app.get('/book/*' , ()=>{
res.end('book')
})
复制代码
这里先配置几条路由,当有url匹配到路由就会执行相对应的回调函数。若是你在回调函数里面调用next(),它还会继续向下找符合的项,并执行相应的回调函数。这里看到express用起来真的很方便,但它内部是如何实现的呢?数组
最初的猜想:数据结构
1.必定是先把这些配置项存储起来,把每个路由的regexp和相应的回调函数看成一个对象存储起来,最后存储到一个数组中。app
2.当有请求过来,遍历数组,拿出url和每个路由的regexp匹配,匹配成功了执行相应的回调函数。函数
3.next:当第2步完成后,若是调用next,执行了相应的回调函数后不停,继续向下遍历数组,执行相应的逻辑。若是没有调用next, 就直接停了。post
console.log(app),找到了我想要的东西,app._router里面存储的是各个配置项的信息。this
以后作的事,就是看源码,看如何存的了。既然我是经过app.get()方法配置的,直接找app.get()相应的源码就行了。url
先是打开入口文件,这个很好找,是express.js.(源码1)spa
这里,经过mixin(app, proto, false)看出来,app的方法都是写在proto里面,也就是都写在application.js里面。直接在application.js里面搜索app.get,然而并无搜到。。。以后我发现,原来express是这么作的。(源码2)3d
果真在这里看到了, router.stack里面应该装的就是各个layer了😊.(后面会说到,layer就是装着每个路由项,包括regexp,回调函数等)。不过如今尚未装呢,没执行this.stack.push()操做呢。还得往回看,回到(源码2),刚刚只是执行了this.lazyrouter();。以后执行了var route = this._router.route(path);这步开始装layer了。接下来挺重要的了。this._router.route(path),也就是./router/index.js下(源码5)
再贴一次layer对象的数据结构
再看看刚刚那个route对象,构造函数在./router/route.js里面(源码7)
在哪里装的??? 让咱们回到(源码2)
route[method].apply(route, slice.call(arguments, 1));
这行代码,真正的执行代码在./router/route.js里面(源码8)
先整理一下: app._router,也就是最大的那个对象,里面会有一个stack数组,装着经过app.get('/','callback')等建立出的layer,这种layer(有路由的,就是经过app.get()等方法建立出来的),会有一个handle,这个handle==route.dispatch.bind(route).同时会有一个route,里面一样会有一个stack,这里面的layer的handle撞着真实的路由回调函数。以下图
接下来再看经过app.use()建立的中间件.和上面差不过,直接看代码,再./router/index.js(源码9)
app._router.stack = [layer,layer...]; 里面装着2种类型的layer。
第一种是经过app.get()等方式建立的layer.这种layer,handle==route.dispatch.bind(route)。layer.route = [layer..].同时route里面装着layer(layer.handle==真正的路由回调函数)。
第二种是经过app.use()建立的layer,这种layer,layer.handle就是真正的回调函数。同时layer.route=undefined;
第一步的源码过程看着会比较枯燥
接下来看代码的过程就是从有请求来的时候,那部分代码看就能够了
仍是看express.js里面的代码 , 源码(2-1)
app.handle实现代码在application.js里面, 源码(2-2)
而后在经过router.handle(req,res,done);也就是在router文件夹下的index.js 核心代码了, 源码(2-3)
能够看到stack也就是app._router.stack里面装的是各类layer,有经过app.get()等方式建立的带route的layer,也有经过app.use()建立的layer.route=undefined的layer.
以后在里面写了一个next方法,(据猜想就是回调函数里面的第三个参数next), 并执行了next();能够看到里面作的事情就是开始遍历layer数组,直到先找到的第一个match到的layer,以后会走到layer.handle_request(req, res, next)。这里面的next参数就是上面proto.handle里面定义的next函数
layer.handle_request()的源码在./router/layer.js里面.(源码2-4)
这里要当心一点了 ,fn = this.handle. 上面说过,layer有两种类型,一种是有路由的layer,layer.handle == route.dispatch.bind(route).另外一种是app.use()那种,layer.handle就是回调函数。
先说第二种,若是先匹配到的layer是app.use()那种,此时执行fn(req,res,next),也就直接执行了回调函数。若是在回调函数里面调用了next()方法,也就是在走到上面proto.handle里面的next(),继续遍历stack数组的下一项,周而复始,直到遍历完。。(一块儿都看着那么的顺畅)
再来看第一种:执行fn(req,res,next);这时候走到的是route.dispatch.bind(route)这个函数里。(讲真的,这个函数真心重要)
上代码,route.dispatch的源码在./router/route.js (源码2-5)
这里能够看到,这个对象也就是带路由得那种layer,里面的lauer.route对象,以下图
这里能够看到,stack里面装的layer就是,layer.handle==真正的回调函数那种了。以后执行layer.handle_request(req, res, next),走的过程也就是第二种路由走的过程了。
可是会很奇怪,为何在这里又定义了一个next()函数??
答案:这里面(也就是带路由的layer,layer.route.stack),stack一样是一个数组,这个数组里的layer,layer里面的handle== 真正路由回调函数,因此须要先把这个数组的layer遍历完,执行相应的layer.handle,而后再回到app._router.stack,继续遍历app._router.stack。(固然前提是都执行next,不执行next直接就停了啊)。
再贴段代码吧,layer.route.stack里面的两个layer分别装这两个回调函数。
app.get('/',(req,res,next)=>{
console.log(1);
next()
},(req,res,next)=>{
console.log(2);
next();
})
复制代码
因此这个next是为了遍历路由layer里面的stack,等都执行完,再去执行app._router.stack。
具体咋作的呢,继续看代码:dispatch函数的第三个函数done实际上就是proto.handle里面的next.在112行,随着在路由layer里面执行next后 , idx++; 等到数组满了,也就会走到done函数了,也就会继续遍历app._route.stack里面的layer了。
也就是说有两个next , 一个是最上面那个, proto.handle里面的next方法 ,控制着app._router.stack里面的layer, 一个是route.dispatch里面定义的next方法 ,控制着路由layer里面的route.stack里面的layer。也就是说经过app.get方法的回调函数里面的第三个参数next的定义是route.dispatch里面定义的next方法,app.use的回调函数的第三个参数next是proto.handle里面定义的next方法。
语文不太好 ,写的有点‘冗余’,但愿感兴趣的同窗认真看 ,能获得帮助