这是我参与更文挑战的第14天,活动详情查看:更文挑战
介绍
koa-convart
是用于兼容 koa1
与 koa2
的一个工具库。koa 0.x
以及 1.x
版本的中间件是 generator
函数形式,而 koa 2.x
的中间件是 promise
的形式。通过以下代码对比一下:
// koa 1.x 示例
var koa = require('koa');
var app = koa();
app.use(function *(next){
var start = new Date;
yield next;
//再次进入中间件,记录2次通过此中间件「穿越」的时间
var ms = new Date - start;
this.set('X-Response-Time', ms + 'ms');
});
app.use(function *(){
this.body = 'Hello World';
});
app.listen(3000);
//=================================================
// koa 2.x 示例
const Koa = require('koa');
const app = new Koa();
app.use(async (ctx, next) => {
const start = Date.now();
await next();
const ms = Date.now() - start;
ctx.set('X-Response-Time', `${ms}ms`);
});
app.use(async ctx => {
ctx.body = 'Hello World';
});
app.listen(3000);
复制代码
通过对比可以看到,旧版本的中间件是 generator
函数,接受一个 next
方法,而新版本的中间件则是以async
函数(本质是一个promise
),接受两个参数:上下文 ctx
和 next
方法。
koa-convert
所实现的功能就是,使 koa2
能够兼容旧版的中间件,或者使 koa1
能够使用新版的中间件,另外还提供了组合新老中间件的 api
。使用示例如下:
const Koa = require('koa') // koa v2.x
const convert = require('koa-convert')
const app = new Koa()
app.use(modernMiddleware)
app.use(convert(legacyMiddleware))
app.use(convert.compose(legacyMiddleware, modernMiddleware))
function * legacyMiddleware (next) {
// before
yield next
// after
}
function modernMiddleware (ctx, next) {
// before
return next().then(() => {
// after
})
}
复制代码
源码分析
koa-convert
源码代码量很少,包括注释和空行总共 105 行,核心代码只有几十行,包括三个 api
方法:convert()
、convert.compose
、convert.back
。
convert
我们先来看convert()
的源码实现,逻辑如下:
convert
方法首先判断传入的中间件是否是一个函数,如果不是就抛出异常;- 接着判断是否是一个
generator
函数,如果不是就直接返回不做处理; - 利用
co
将generator
函数形式的中间件转成promise
形式。
co
相关知识,可以翻阅我写的源码系列—co进行过学习。
function convert (mw) {
// 不是函数抛出异常
if (typeof mw !== 'function') {
throw new TypeError('middleware must be a function')
}
// 不是 generator 函数,直接返回
if (
mw.constructor.name !== 'GeneratorFunction' &&
mw.constructor.name !== 'AsyncGeneratorFunction'
) {
return mw
}
// 通过 co 进行转换
const converted = function (ctx, next) {
return co.call(
ctx,
mw.call(
ctx,
(function * (next) { return yield next() })(next) // 返回下一个中间件并作为参数传入
))
}
converted._name = mw._name || mw.name
return converted
}
复制代码
convert.compose
接着来看 convert.compose
的实现。
convert.compose
用于将多个中间件合成一个中间件,其中使用了 koa-compose
工具库,该工具库用于将多个中间件进行洋葱模型的组合,详细知识可以通过我写的这篇源码系列—koa-compose进行学习了解。
convert.compose = function (arr) {
// 首先判断入参形式,接受一个数组或者是多个参数
// convert.compose(mw, mw, mw)
// convert.compose([mw, mw, mw])
if (!Array.isArray(arr)) {
// 如果是多个参数就通过 Array.from 转成数组
arr = Array.from(arguments)
}
// 将数组的每一项都通过 convert 转换成 promise 形式的中间件
// 然后调用 compose 进行洋葱模型组合
return compose(arr.map(convert))
}
复制代码
convert.back
最后看一下 convert.back
的实现。
convert.back
用于将 promise
中间件转换成 generator
函数形式的中间件,使 koa2
的中间件能够在 koa1
中使用。源码逻辑如下:
- 判断是否是函数,不是函数就抛出错误异常;
- 判断是否是 generator 函数,是则直接返回;
- 将
mw
包装成一个generator
函数,使用co
将generator
函数形式的next
转成promise
传给mw
。
convert.back = function (mw) {
if (typeof mw !== 'function') {
throw new TypeError('middleware must be a function')
}
if (mw.constructor.name === 'GeneratorFunction' || mw.constructor.name === 'AsyncGeneratorFunction') {
return mw
}
// 用 generator 函数对中间件进行封装
const converted = function * (next) {
const ctx = this
let called = false // 用于判断 next 是否被调用了两次
yield mw(ctx, function () {
if (called) {
throw new Error('next() called multiple times')
}
called = true
// mw 是 promise 中间件,接收的 next 也是 promise
// 此时传递进来的 next 是 generator,需要用 co 转换一下
return co.call(ctx, next)
})
}
converted._name = mw._name || mw.name
return converted
}
复制代码
相关资料
© 版权声明
文章版权归作者所有,未经允许请勿转载。
THE END