目前比较流行的nodejs
框架有express
、koa
、egg.js
,还有就是和ts相关的框架nest.js
。
无论是哪种框架,其核心都是基于中间件来实现的,而中间件执行的方式都跟洋葱模型
有关,它们的差别主要也是在洋葱模型的执行方式上。
什么是洋葱模型?
洋葱模型,就像洋葱一样,一层包裹一层,而nodejs框架的执行就像是中间穿过洋葱的一条线,而每一层洋葱皮就代表一个中间件,进入时穿过多少层,出来时还得穿出多少层,具有先进后出(栈)的特点。
![](https://img.haomeiwen.com/i12188741/b52627362cdc4a26.png)
穿进来时:middleware1
-> middleware2
-> middleware3
-> center
穿出来时:center
-> middleware3
-> middleware2
-> middleware1
- 穿进来时,中间件的切换主要靠
next()
关键字来实现的 - 穿出来时,则是按中间件执行完毕后,按照原路径反回去
Express
Express
在 Node.js
初期就是一个热度较高、成熟的 Web 框架,并且包括的应用场景非常齐全。同时基于 Express
,也诞生了一些场景型的框架,常见的就如上面我们提到的 Nest.js
框架
KOA
随着nodejs发展,出现了以await/async
为核心的语法糖,Express
原班人马为了实现一个高可用、高性能、更健壮,并且符合当前Node.js
版本的框架,开发出了可定制的 KOA
框架。
Egg.js
就是在 KOA
基础上,做了各种比较成熟的中间件和模块,可以说是在 KOA
框架基础上的最佳实践,用以满足开发者开箱即用的特性。
所以在对比差异时,我们主要对比Express
和KOA
就可以看出它们间的主要区别
Express和KOA的差异
-
Express
封装、内置了很多中间件,比如connect
和router
,而KOA
则比较轻量,开发者可以根据自身需求定制框架; -
Express
是基于callback
来处理中间件的,而KOA
则是基于await/async
; - 在异步执行中间件时,
Express
并非严格按照洋葱模型执行中间件,而KOA
则是严格遵循的。 -
Express
使用callback
捕获异常,对于深层次的异常捕获不了,Koa
使用try catch
,能更好地解决异常捕获。
下面来以例子说明一下两个框架的执行过程不一样的地方:
Express示例
- 写个express服务器
- app.use:用于中间件和路由的处理。当参数是函数时,匹配所有路由;当参数是字符串时,匹配具体对应的路由
mkdir node-demo
cd node-demo
npm init -y
mkdir src
touch express.js
const express = require('express')
const app = express()
const port = 3000
const server = app.listen(port, () => {
const host = server.address().address
const port = server.address().port
console.log(`Express server is listening on ${host}:${port}!`)
})
app.use((req, res, next) => {
console.log('middleware1 start')
next(); // 跳到下一层洋葱中间件
console.log('middleware1 end')
})
app.use((req, res, next) => {
console.log('middleware2 start')
next(); // 跳到下一层洋葱中间件
console.log('middleware2 end')
})
app.use((req, res, next) => {
console.log('middleware3 start')
next(); // 跳到下一层洋葱中间件
console.log('middleware3 end')
})
app.get('/', (req, res) => {
res.send('hello express')
})
- 启动服务
node ./src/express-test.js
- 在浏览器访问http://localhost:3000/,结果如下:
express服务器
- 关闭和管理服务
关闭服务的操作:在当前目录下ctrl+c
结束进程
KOA示例
KOA的用法其实和Express差不多,只不过Express内置了router,而KOA需要自己定制:
- 安装:
npm i koa koa-router chalk
- 中间件的方法和Express基本一样,只不过将
req, res
参数换成了上下文ctx
- 设置路由需要用路由实例方法
router.get
方式,且要把路由实例设置到koa实例上app.use(router.routes())
const chalk = require('chalk')
const Koa = require('koa')
const Router = require('koa-router')
const app = new Koa()
const router = new Router()
const port = 9000
// 使用ctx上下文
app.use((ctx, next) => {
console.log('middleware1 start')
next(); // 跳到下一层洋葱中间件
console.log('middleware1 end')
})
app.use((ctx, next) => {
console.log('middleware2 start')
next(); // 跳到下一层洋葱中间件
console.log('middleware2 end')
})
app.use((ctx, next) => {
console.log('middleware3 start')
next(); // 跳到下一层洋葱中间件
console.log('middleware3 end')
})
router.get('/', (ctx) => {
ctx.body = 'Hello koa!'
})
// 使用router
app.use(router.routes())
app.listen(port, 'localhost', () => {
console.log(chalk.yellow(`Express server is listening on ${port}!`))
})
![](https://img.haomeiwen.com/i12188741/2b11f137274806be.png)
可以看到如果是中间件中的代码是同步的时候,两者的的执行顺序是一样的。现在修改一下:
- 增加一个执行异步操作的中间件
- 每一个中间件,都增加上
async await
处理
// 这里只举一例子,其它的中间件是一样的
app.use(async (req, res, next) => {
console.log('middleware1 start')
await next(); // 跳到下一层洋葱中间件
console.log('middleware1 end')
})
app.use(async (req, res, next) => {
console.log('async start');
await next();
await new Promise(
(resolve) =>
setTimeout(
() => {
console.log(`wait 1000 ms end`);
resolve()
},
1000
)
);
console.log('async end');
});
此时,再观察两者的输出顺序:
Express
是顺序不是严格按照洋葱模型的:
![](https://img.haomeiwen.com/i12188741/7b15bd6a4e9a29eb.png)
KOA
的顺序是严格按照洋葱模型的:
![](https://img.haomeiwen.com/i12188741/3c43bb4160dd442e.png)
发生这样的原因是两者基于的nodejs的版本不一样导致的。
参考:
https://blog.csdn.net/xgangzai/article/details/109108387
https://www.jianshu.com/p/6f7930687835/
网友评论