- pauseTracking/resetTracking:读但不订阅(依赖收集开关,支持嵌套)。
- pauseScheduling/resetScheduling:先做完一批,再统一调度(调度合并)。
pauseTracking/resetTracking(读但不订阅)
说明:模拟一个极简依赖收集系统;对比“未暂停 vs 暂停 tracking”时,hook 内读取是否会被收集为依赖。
let sholudTrack = true
trackStack = []
let activeEffect = null
const dep = new Set()
function pauseTracking(){
trackStack.push(sholudTrack)
sholudTrack = false
}
function resetTracking(){
const last = trackStack.pop()
shouldTrack = last === undefined ? true : last
}
funtion track(){
if(shouldTrack && activeEffect) dep.add(activeEffect())
}
function trigger(){
dep.forEach((e) => e());
}
const state = {
_count: 0,
get count(){
track()
return this._count
}
set count(v){
trigger()
this._count = v
}
}
function effect(fn){
const runner = ()=>{
activeEffect = runner;
fn();
activeEffect = null;
}
runner()
return runner
}
effect(() => {
console.log('effect A start');
hookReadsCount('hook A');
console.log('effect A end');
});
console.log('--- 修改 count,将触发 A(含 hook 的那次读取) ---');
state.count++;
effect(() => {
console.log('effect B start');
pauseTracking();
hookReadsCount('hook B');
resetTracking();
console.log('effect B end');
});
console.log('--- 再次修改 count,仅触发 B 中真正订阅的读取 ---');
state.count++;
要点:
- 未暂停时,hook 内读取被“当前活跃 effect”订阅(可能是误收集)。
- 暂停后,同样读取不再被收集,避免误订阅、依赖污染、潜在循环。
pauseScheduling/resetScheduling(调度合并)
模拟调度器队列;对比“暂停期间多次入队仅在恢复时统一 flush 一次”,体现批处理。
let pauseScheduleStack = 0
const queue = []
function pauseScheduling = ()=>{
pauseScheduleStack++
}
function resetSchduling = ()=>{
pauseScheduleStack--
if(!pauseScheduleStack)&&flush();
}
function schedule = (job)=>{
queue.push(job)
if(!pauseScheduleStack)&&flush();
}
function flush(){
if(!queue.length) return
const jobs = queue.splice(0);
console.log('flush once, jobs =', jobs.length);
for (const j of jobs) j();
}
const job = () => console.log('run effect scheduler');
console.log('--- 未暂停:每次 schedule 立即 flush ---');
schedule(job);
schedule(job);
console.log('--- 暂停调度:批量入队,恢复时统一 flush 一次 ---');
pauseScheduling();
schedule(job);
schedule(job);
schedule(job);
console.log('(此时未 flush)');
resetScheduling();
- 暂停期间,多次 schedule 仅入队不执行;恢复时批量 flush,一次完成,减少抖动。
- 在 Vue 内部,数组/Map/Set 的“写方法”常用 pauseTracking + pauseScheduling 组合:既避免写过程中内部“读”被误收集,也把多次变动合并为一次调度。