手写@koa/router源码 (4)

这里/就匹配了两个回调函数,但是你如果这么写,你会得到一个Not Found。为什么呢?因为你第一个回调里面没有调用next()!前面说了,这里的next()是dispatch(i + 1),会去调用layerChain里面的下一个回调函数,换一句话说,你这里不调next()就不会运行下一个回调函数了!要想让/返回Hello World,我们需要在第一个回调函数里面调用next,像这样:

router.get("http://www.likecs.com/", (ctx, next) => { console.log("123"); next(); // 记得调用next }); router.get("http://www.likecs.com/", (ctx, next) => { ctx.body = "Hello World"; });

所以有朋友觉得@koa/router回调函数里面的next没什么用,如果你一个路由只有一个匹配的回调函数,那确实没什么用,但是如果你一个路径可能匹配多个回调函数,记得调用next。

router.routes官方源码:

router.match()

上面router.routes的源码里面我们用到了router.match这个实例方法来查找所有匹配的layer,上面是这么用的:

const matched = router.match(path, ctx.method);

所以我们也需要写一下这个函数,这个函数不复杂,通过传入的path和method去router.stack上找到所有匹配的layer就行:

Router.prototype.match = function (path, method) { const layers = this.stack; // 取出所有layer let layer; // 构建一个结构来保存匹配结果,最后返回的也是这个matched const matched = { path: [], // path保存仅仅path匹配的layer pathAndMethod: [], // pathAndMethod保存path和method都匹配的layer route: false, // 只要有一个path和method都匹配的layer,就说明这个路由是匹配上的,这个变量置为true }; // 循环layers来进行匹配 for (let i = 0; i < layers.length; i++) { layer = layers[i]; // 匹配的时候调用的是layer的实例方法match if (layer.match(path)) { matched.path.push(layer); // 只要path匹配就先放到matched.path上去 // 如果method也有匹配的,将layer放到pathAndMethod里面去 if (~layer.methods.indexOf(method)) { matched.pathAndMethod.push(layer); if (layer.methods.length) matched.route = true; } } } return matched; };

上面代码只是循环了所有的layer,然后将匹配的layer放到一个对象matched里面并返回给外面调用,match.path保存了所有path匹配,但是method并不一定匹配的layer,本文并没有用到这个变量。具体匹配path其实还是调用的layer的实例方法layer.match,我们后面会来看看。

这段代码还有个有意思的点是检测layer.methods里面是否包含method的时候,源码是这样写的:

~layer.methods.indexOf(method)

而一般我们可能是这样写:

layer.methods.indexOf(method) > -1

这个源码里面的~是按位取反的意思,达到的效果与我们后面这种写法其实是一样的,因为:

~ -1; // 返回0,也就是false ~ 0; // 返回-1, 注意-1转换为bool是true ~ 1; // 返回-2,转换为bool也是true

这种用法可以少写几个字母,又学会一招,大家具体使用的还是根据自己的情况来吧,选取喜欢的方式。

router.match官方源码:

layer.match()

上面用到了layer.match这个方法,我们也来写一下吧。因为我们在创建layer实例的时候,其实已经将path转换为了一个正则,我们直接拿来用就行:

Layer.prototype.match = function (path) { return this.regexp.test(path); };

layer.match官方源码:

总结

到这里,我们自己的@koa/router就写完了,使用他替换官方的源码也能正常工作啦~

本文可运行代码已经上传到GitHub,大家可以拿下来玩玩:https://github.com/dennis-jiang/Front-End-Knowledges/tree/master/Examples/Node.js/KoaRouter

最后我们再来总结下本文的要点吧:

@koa/router整体是作为一个Koa中间件存在的。

@koa/router是fork的koa-router继续进行维护。

@koa/router的整体思路跟Express.js路由模块很像。

@koa/router也可以分为注册路由匹配路由两部分。

注册路由主要是构建路由的数据结构,具体来说就是创建很多layer,每个layer上保存具体的path,methods,和回调函数。

@koa/router创建的数据结构跟Express.js路由模块有区别,少了route这个层级,但是个人觉得@koa/router的这种结构反而更清晰。Express.js的layer和route的相互引用反而更让人疑惑。

匹配路由就是去遍历所有的layer,找出匹配的layer,将回调方法拿来执行。

一个路由可能匹配多个layer和回调函数,执行时使用koa-compose将这些匹配的回调函数串起来,一个一个执行。

需要注意的是,如果一个路由匹配了多个回调函数,前面的回调函数必须调用next()才能继续走到下一个回调函数。

参考资料

@koa/router官方文档:https://github.com/koajs/router

@koa/router源码地址:https://github.com/koajs/router/tree/master/lib

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/wpsjzz.html