详解KOA2如何手写中间件(装饰器模式)(4)

从上面看出 koa-router 导出的是一个类,使用时需要创建一个实例,并且调用实例的 routes 方法将该方法返回的 async 函数进行连接,但是在匹配路由的时候,会根据路由 get 方法中的路径进行匹配,并串行执行内部的回调函数,当所有回调函数执行完毕之后会执行整个 Koa 串行的 next ,原理同其他中间件,我下面来针对上面使用的功能简易实现。

文件:my-koa-router.js

// 控制每一个路由层的类 class Layer { constructor(path, cb) { this.path = path; this.cb = cb; } match(path) { // 地址的路由和当前配置路由相等返回 true,否则返回 false return path === this.path; } } // 路由的类 class Router { constructor() { // 存放每个路由对象的数组,{ path: /xxx, fn: cb } this.layers = []; } get(path, cb) { // 将路由对象存入数组中 this.layers.push(new Layer(path, cb)); } compose(ctx, next, handlers) { // 将匹配的路由函数串联执行 function dispatch(index) { // 如果当前 index 个数大于了存储路由对象的长度,则执行 Koa 的 next 方法 if(index >= handlers.length) return next(); // 否则调用取出的路由对象的回调执行,并传入一个函数,在传入的函数中递归 dispatch(index + 1) // 目的是为了执行下一个路由对象上的回调函数 handlers[index].cb(ctx, () => dispatch(index + 1)); } // 第一次执行路由对象的回调函数 dispatch(0); } routes() { return async (ctx, next) { // 当前 next 是 Koa 自己的 next,即 Koa 其他的中间件 // 筛选出路径相同的路由 let handlers = this.layers.filter(layer => layer.match(ctx.path)); this.compose(ctx, next, handlers); } } }

在上面我们创建了一个 Router 类,定义了 get 方法,当然还有 post 等,我们只实现 get 意思一下, get 内为逻辑为将调用 get 方法的参数函数和路由字符串共同构建成对象存入了数组 layers ,所以我们创建了专门构造路由对象的类 Layer ,方便扩展,在路由匹配时我们可以根据 ctx.path 拿到路由字符串,并通过该路由过滤调数组中与路由不匹配的路由对象,调用 compose 方法将过滤后的数组作为参数 handlers 传入,串行执行路由对象上的回调函数。

compose 这个方法的实现思想非常的重要,在 Koa 源码中用于串联中间件,在 React 源码中用于串联 redux 的 promise 、 thunk 和 logger 等模块,我们的实现是一个简版,并没有兼容异步,主要思想是递归 dispatch 函数,每次取出数组中下一个路由对象的回调函数执行,直到所有匹配的路由的回调函数都执行完,执行 Koa 的下一个中间件 next ,注意此处的 next 不同于数组中回调函数的参数 next ,数组中路由对象回调函数的 next 代表下一个匹配路由的回调。

总结

上面我们分析和模拟了一些中间件,其实我们会理解 Koa 和 Express 相比较的优势是没有那么繁重,开发使用方便,需要的功能都可以用对应的中间件来实现,使用中间件可以给我们带来一些好处,比如能将我们处理好的数据和新方法挂载在 ctx 上,方便后面 use 传入的回调函数中使用,也可以帮我们处理一些公共逻辑,不至于在每一个 use 的回调中都去处理,大大减少了冗余代码,由此看来其实给 Koa 使用中间件的过程就是一个典型的 “装饰器” 模式,在通过上面的分析之后相信大家也了解了 Koa 的 “洋葱模型” 和异步特点,知道该如何开发自己的中间件了。

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

转载注明出处:http://www.heiqu.com/24763fb3ff4d0edb870965d4e62b2e48.html