Koa2 源码理解
小布丁 2020-12-09 Node
# Koa2 源码理解
koa官网
koa1核心基于generator
,但是严重依赖co的包装 koa2完全不需要,基于async/await
其实质也是generator
的语法糖调用包装 但是很方便使用,在node v7 以上可直接运行
Koa基于Node的原生http模块的再度上层封装,是基于中间件 模式的一种封装,最显著的特性就是 洋葱模型 的概念深入人心
# 1.如何理解洋葱模型
这里提出一个概念 面向切面编程(AOP), 怎么定义或者解读这个概念呢?
**面向切面的编程(AOP)是一种用于以 非侵入方式增强对象,方法和功能的行为的技术 AOP允许添加新行为以及 从外部 组合和修改现有行为 。
听起来很脑胀完全不懂的样子,举个栗子
农场的水果包装流水线一开始只有 采摘 - 清洗 - 贴标签
三个顺序操作
为了提高销量,想加上两道工序 分类
和 包装
但又不能干扰原有的流程,同时如果没增加收益可以随时撤销新增工序
最后在流水线的中的空隙插上两个工人去处理,形成采摘 - 分类 - 清洗 - 包装 - 贴标签
的新流程,而且工序可以随时撤回
大体的可以说 AOP 是程序生命周期或者流程中 加入/减去
一个或者多个功能,不影响原有功能的方法。 AOP编程思想中需要关注的就是切面的位置 一般有两个切面 一个是前置切面 一个是后置切面
# Koa洋葱模型
# Koa洋葱模型中的AOP
- Koa中的切面则是由中间件机制实现的
- 一个中间件一般有两个切面
- 遵循先进后去的切面执行顺序,类型入栈出栈的顺序
# 2.中间件机制
let context = {
data: []
};
async function middleware1(ctx, next) {
console.log('action 001');
ctx.data.push(1);
await next();
console.log('action 006');
ctx.data.push(6);
}
async function middleware2(ctx, next) {
console.log('action 002');
ctx.data.push(2);
await next();
console.log('action 005');
ctx.data.push(5);
}
async function middleware3(ctx, next) {
console.log('action 003');
ctx.data.push(3);
await next();
console.log('action 004');
ctx.data.push(4);
}
Promise.resolve(middleware1(context, async() => {
return Promise.resolve(middleware2(context, async() => {
return Promise.resolve(middleware3(context, async() => {
return Promise.resolve();
}));
}));
})).then(() => {
console.log('end');
console.log('context = ', context);
});
// 结果显示
// "action 001"
// "action 002"
// "action 003"
// "action 004"
// "action 005"
// "action 006"
// "end"
// "context = { data: [1, 2, 3, 4, 5, 6]}"
源码元素解析
- 生命周期就是 `Promise.resolve` 的嵌套
- 中间件就是 `middleware1`、`middleware2`和`middleware3`
- 中间件在生命周期中,就是 `Promise.resolve(middleware)`嵌套中执行中间件
- `middleware1` 前置操作 `action 001`
- 等待嵌套的 `middleware2`
- `middleware2` 前置操作 `action 002`
- 等待嵌套的 `middleware3`
- `middleware3` 前置操作 `action 003`
- `middleware3` 后置操作 `action 004`
- `middleware2` 后置操作 `action 005`
- `middleware1` 后置操作 `action 006`
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
57
58
59
60
61
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
57
58
59
60
61
处理过程简化入下图所示
+----------------------------------------------------------------------------------+
| |
| middleware 1 |
| |
| +-----------------------------------------------------------+ |
| | | |
| | middleware 2 | |
| | | |
| | +---------------------------------+ | |
| | | | | |
| action | action | middleware 3 | action | action |
| 001 | 002 | | 005 | 006 |
| | | action action | | |
| | | 003 004 | | |
| | | | | |
+---------------------------------------------------------------------------------------------->
| | | | | |
| | | | | |
| | +---------------------------------+ | |
| +-----------------------------------------------------------+ |
+----------------------------------------------------------------------------------+
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
# 3.Koa中的中间件
熟悉使用Koa的小伙伴都知道 app.use()
方法是使用中间件 ,下面代码是Koa 最最简单的应用了
const Koa = require('koa');
let app = new Koa();
const middleware1 = async (ctx, next) => {
console.log(1);
await next();
console.log(6);
}
const middleware2 = async (ctx, next) => {
console.log(2);
await next();
console.log(5);
}
const middleware3 = async (ctx, next) => {
console.log(3);
await next();
console.log(4);
}
app.use(middleware1);
app.use(middleware2);
app.use(middleware3);
app.use(async(ctx, next) => {
ctx.body = 'hello world'
})
app.listen(3001)
// 控制台会出现以下结果
// 1
// 2
// 3
// 4
// 5
// 6
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
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
Koa的中间件引擎是 koa-compose
模块来实现的 中间件引擎需要处理的事情:
- 维护中间件队列
- 处理中间件队列,并将上下文传递进去
- 中间件的流程控制器 next
- 异常处理
核心源码
module.exports = compose;
// middleware 中间件队列
function compose(middleware) {
if (!Array.isArray(middleware)) {
throw new TypeError('Middleware stack must be an array!');
}
// 返回一个函数,在此调用的时候 传递上下 ctx
return function (ctx, next) {
let index = -1;
return dispatch(0);
function dispatch(i) {
if (i < index) {
return Promise.reject(new Error('next() called multiple times'));
}
index = i;
let middleFn = middleware[i];
if (i === middleware.length) {
middleFn = next;
}
if (!middleFn) {
return Promise.resolve();
}
// middleFn 就是中间件函数
// middleFn = async(ctx, next) => {
// console.log('action 002');
// ctx.data.push(2);
// await next();
// console.log('action 005');
// ctx.data.push(5);
// }
try {
return Promise.resolve(
middleFn(ctx, () => {
return dispatch(i + 1);
}));
} catch (err) {
return Promise.reject(err);
}
}
};
}
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
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