批量更新实现
# 概述
在vue3响应式系统设计中,批量更新是优化性能的核心机制之一。当短时间内频繁多次修改响应式数据时,批量更新可以避免频繁触发订阅者的更新操作,将这些更新操作合并为一次,从而减少不必要的计算和DOM操作。
批量更新也是利用链表的方式实现。
# 批量更新的实现
# 核心变量
批量更新的实现依赖于3个核心变量:batchDepth
、batchedSub
和batchedComputed
let batchDepth = 0; // 批量更新的嵌套深度
let batchedSub;// 存储待执行的普通订阅者队列
let batchedComputed; // 存储待执行的计算属性computed订阅者队列(单独处理,有优先级)
1
2
3
2
3
# 核心方法
批量更新的实现依赖于3个核心方法:startBatch
、endBatch
和batch
# batch
batch
方法的作用是将订阅者sub
加入批量队列中。sub
通常是ReactiveEffect
实例,该方法不会立即执行更新,只是暂存将sub
加入批量队列中,等待endBatch
方法调用时统一执行。
batch
方法的实现如下:
function batch(sub, isComputed = false) {
// 标记订阅者为 EffectFlags.NOTIFIED "已加入批量队列"
sub.flags |= 8;
if (isComputed) {
// 若为计算属性订阅者,则加入计算属性链表中(单独维护)
sub.next = batchedComputed; // 新订阅者放在链表的头部
batchedComputed = sub;
return;
}
// 若为普通订阅者,则加入普通订阅者链表中
sub.next = batchedSub;
batchedSub = sub;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
2
3
4
5
6
7
8
9
10
11
12
13
# startBatch
startBatch
方法的作用是开启批量更新,将batchDepth
加1
,说明后续的更新操作会被暂存,不立即执行。
function startBatch() {
batchDepth++;
}
1
2
3
2
3
# endBatch
endBatch
就是当所有嵌套的批量更新都结束后(batchDepth
减为0),执行链表中所有暂存的订阅者更新,并清理状态。
endBatch
的源码实现如下:
function endBatch() {
// 若批量更新仍有嵌套(深度未到0),不执行实际更新
if (--batchDepth > 0) {
return;
}
// 处理计算属性订阅者链表
if (batchedComputed) {
let e = batchedComputed;
batchedComputed = void 0; // 清空队列
while (e) {
const next = e.next;
e.next = void 0;// 重置链表指针
e.flags &= -9;// 清除批量队列的标志
e = next;
}
}
// 处理普通订阅者队列并执行更新
let error;
while (batchedSub) {
let e = batchedSub;
batchedSub = void 0; // 清空队列
while (e) {
const next = e.next;
e.next = void 0; // 重置链表指针
e.flags &= -9; // 清除批量队列的标志
// 若订阅者标记为需要触发更新,则执行更新,调用订阅者的trigger方法
if (e.flags & 1) {
try {
;
e.trigger();
} catch (err) {
// 暂存错误
if (!error) error = err;
}
}
// 遍历下一个订阅者
e = next;
}
}
// 处理错误
if (error) throw error;
}
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
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
编辑 (opens new window)
上次更新: 2025/08/26, 10:34:53