Vue3源码reactivity响应式篇之批量更新
概述
在vue3响应式系统设计中,批量更新是优化性能的核心机制之一。当短时间内频繁多次修改响应式数据时,批量更新可以避免频繁触发订阅者的更新操作,将这些更新操作合并为一次,从而减少不必要的计算和DOM操作。
批量更新也是利用链表的方式实现。
批量更新的实现
核心变量
批量更新的实现依赖于3个核心变量:batchDepth
、batchedSub
和batchedComputed
let batchDepth = 0; // 批量更新的嵌套深度
let batchedSub;// 存储待执行的普通订阅者队列
let batchedComputed; // 存储待执行的计算属性computed订阅者队列(单独处理,有优先级)
核心方法
批量更新的实现依赖于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;
}
startBatch
startBatch
方法的作用是开启批量更新,将batchDepth
加1
,说明后续的更新操作会被暂存,不立即执行。
function startBatch() {batchDepth++;
}
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;
}