概述
在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;
}