就像我们睡觉一样,盖了三床被子,要从外面一层一层展开;躺进去后,再一层一层盖上一样。

koa 中的洋葱圈模型

在koa中,中间件是洋葱圈模型。什么是洋葱圈模型呢?

每一层都是一个中间件,先从外到里执行next前的处理,再由中心向外部执行next之后的处理。每一层都有两次执行时机。

我们先使用koa来看一下中间件执行的时机及过程

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
const Koa = require('koa')
const app = new Koa()

app.use((ctx, next) => {
console.log('中间件1 -- next之前')
next()
console.log('中间件1 -- next之后')
})

app.use((ctx, next) => {
console.log('中间件2 -- next之前')
next()
console.log('中间件2 -- next之后')
})

const port = 3000
app.listen(port, () => {
console.log(`http://localhost:${port}`)
})

执行如图所示

打印结果如下

1
2
3
4
中间件1 -- next之前
中间件2 -- next之前
中间件2 -- next之后
中间件1 -- next之后

简易的洋葱圈模型实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
const { createServer } = require("node:http")
class Koa {
handlers = []
ctx = {}
use(handler) {
if (typeof handler !== "function") {
throw new Error("middleware is must be a function")
}

this.handlers.push(handler)
}

_execute() {
if (this.handlers.length === 0) return
const first = this.handlers.shift()

first(this.ctx, () => {
this._execute()
})
}

listen(port, callback) {
const app = createServer((req, res) => {
this._execute()

res.writeHead(200, { "Content-type": "application/json" })
res.end(
JSON.stringify({
status: 200,
message: "hello my koa",
})
)
})

app.listen(port, typeof callback === "function" ? callback : null)
}
}

const app = new Koa()

app.use((ctx, next) => {
console.log("中间件1 -- next之前")
next()
console.log("中间件1 -- next之后")
})

app.use((ctx, next) => {
console.log("中间件2 -- next之前")
next()
console.log("中间件2 -- next之后")
})

const port = 3000
app.listen(port, () => {
console.log(`http://localhost:${port}`)
})