express 和 koa 都是同一个团队弄出来的web 框架,分版本的目的是为了向 es6的标准靠齐。接下来我们就来聊聊这同一个团队因为什么样的事情,要重新做一个 koa 出来
express 出来的时候,js 还是处于混沌期间,es6的标准没有出来。而node的事件处理的方式都是基于 cb(callback) 的这种形式,在当时来看,这种方式是比较好的一种方式。所以express 里面的中间件处理的方式基本上都是回调。es6 的出现,带来了许多新的标准。使得express 不得不考虑需要兼容es6中的语法, 而 es6中处理异步的方式是promise,还有后面陆续的 async 和 await 等语法糖。(express 不是说不能使用这些语法糖,他使用第三方的库是可以解决的,只是没有koa那么优雅)
区别
这里的区别从koa的角度来看,对比 express
更轻量
koa 在express的基础上简化了框架,没有express 那么重,主要表现在下面两个方面:
- koa 不提供内置的中间件;
- koa 不提供路由,而是把路由这个库分离出来了(koa/router)
其实从两者的官网来分析可以看得到,koa 的官网就一个页面,而express向一个包含了各种各样中间件的框架。
结构不一样
express 的对象结构是下面这个样子的。
app.get('/', function (req, res) { res.send('Hello World!') }) 复制代码
用一张图来表示如下:
对于koa来说的话,他比express多了一层context,这样的话对于在保存一些公有的参数的话变得更加合情合理。如下图:
koa 更加友好的支持中间件
为什么说更加友好呢,koa 在写法上支持异步的中间件写法。而对于express 来说的话,功能可以实现,但是在代码阅读上并不是那么的友好
假设我们有这么一个情况,需要 A-> B -> C -> B -> A 这样来执行中间件的顺序来执行同步代码 koa 和 express 没有区别。逻辑如下:
express 同步
app.use(function (req, res, next) { console.log(1); next(); console.log(4); next(); }) // 中间件2 app.use(function (req, res, next) { console.log(3); next(); }) 复制代码
结果
koa 同步
app.use(function (ctx, next) { console.log(1); next(); console.log(4); }); // 中间件2 app.use(function (ctx, next) { console.log(3); next(); }) 复制代码
结果
这两者的代码对于同步处理来说,几乎没有区别。都能实现效果,但是往往写代码过程中,不太现实全是同步的需求。
异步处理
如果我们有这么个逻辑,如下图:
我们想要输出的顺序是 1 -> 2 -> 3 -> 4
express 中的处理
// 模拟异步 function delay(duration) { return new Promise(resolve => { setTimeout(() => { resolve(); }, duration) }) } // 中间件1 app.use(async function (req, res, next) { console.log(1); next(); // 这一段逻辑我本来是需要放到第二个中间件中的,但是放到第二个中间件就实现不了功能了 await delay(1000); console.log(3); console.log(4); next(); }) // 中间件2 app.use(asyncHandler(async function (req, res, next) { console.log(2); })) 复制代码
这里虽然说实现了打印的结果,但是和我们的图中的逻辑是不一样的,是不对的。如果写成和图一样的逻辑。
// 模拟异步 function delay(duration) { return new Promise(resolve => { setTimeout(() => { resolve(); }, duration) }) } // 中间件1 app.use(async function (req, res, next) { console.log(1); await next(); console.log(4); next(); }) // 中间件2 app.use(asyncHandler(async function (req, res, next) { console.log(2); await delay(1000); console.log(3); })) 复制代码
代码是需要这样的逻辑的,但是结果却不一样了,如下:
原有就是express出现的时候,并没有promise和async await 等新的标准,express 可以实现功能,只是用了不一样的方式来做。
koa 实现
// koa 的中间件示例 function delay(duration) { return new Promise(resolve => { setTimeout(() => { resolve(); }, duration) }) } // 中间件1 app.use(async function (ctx, next) { console.log(1); await next(); console.log(4); }) // 中间件2 app.use(async function (ctx, next) { console.log(2); await delay(1000); console.log(3); }) 复制代码
可以看到,对于每个中间件,在完成了一些事情后,可以非常优雅的将控制权传递给下一个中间件,并能够等待它完成,当后续的中间件完成处理后,控制权又回到了自己,这种中间件模型称之为洋葱模型