Vue3 的响应式系统源码级 effect 的调度器(scheduler)机制与任务队列原理
字数 1099 2025-11-21 01:26:08
Vue3 的响应式系统源码级 effect 的调度器(scheduler)机制与任务队列原理
1. 调度器的基本概念
在 Vue3 的响应式系统中,副作用函数(effect)默认在依赖变更时同步执行。但某些场景下需要控制执行时机(如批量更新、异步执行),这时需通过调度器(scheduler)实现。调度器是 effect 的配置选项,允许开发者自定义依赖触发后的执行逻辑。
2. 调度器的源码结构
在 packages/reactivity/src/effect.ts 中,ReactiveEffect 类的构造函数包含调度器参数:
class ReactiveEffect {
constructor(
public fn: Function,
public scheduler?: Function // 可选调度器
) {}
}
当依赖更新触发 trigger 时,会检查 effect 是否有调度器:
// trigger 函数内部逻辑
if (effect.scheduler) {
effect.scheduler(); // 执行调度器逻辑
} else {
effect.run(); // 默认同步执行
}
3. 调度器的工作流程
步骤 1:依赖触发时拦截执行
假设一个 effect 配置了调度器:
const obj = reactive({ count: 0 });
effect(
() => {
console.log(obj.count);
},
{
scheduler: (effect) => {
// 将 effect 加入异步队列
setTimeout(effect);
},
}
);
当修改 obj.count 时,不会立即执行副作用函数,而是执行 scheduler 函数,并将 effect 作为参数传入。
步骤 2:调度器的任务队列管理
Vue3 内部通过任务队列实现批量更新。例如组件更新时,多个数据变更可能触发多个 effect,但通过调度器可将这些 effect 合并到同一个任务队列中:
- 收集任务:在
trigger中,将需要执行的 effect 加入queue队列(避免重复添加)。 - 异步执行:通过
Promise.resolve().then()将队列执行推到微任务队列,实现批量更新。
步骤 3:实际应用——组件更新队列
在 packages/runtime-core/src/scheduler.ts 中,Vue3 维护了一个组件更新队列:
const queue: ReactiveEffect[] = [];
function queueJob(job: ReactiveEffect) {
if (!queue.includes(job)) {
queue.push(job);
queueFlush(); // 触发队列执行
}
}
function queueFlush() {
if (!isFlushing) {
isFlushing = true;
currentFlushPromise = Promise.resolve().then(flushJobs);
}
}
function flushJobs() {
// 执行队列中的所有 effect
for (const job of queue) {
job.run();
}
queue.length = 0; // 清空队列
}
组件渲染的 effect 默认配置了调度器,其 scheduler 即 queueJob 函数。
4. 调度器的优先级与控制
Vue3 允许通过调度器实现任务优先级控制:
- 前置任务:某些 effect 需在组件更新前执行(如
watch的回调)。 - 后置任务:如
updated生命周期钩子在组件更新后执行。
在scheduler.ts中,队列分为pre(前置)、normal(正常)、post(后置)三个队列,按顺序执行。
5. 总结:调度器的核心作用
- 控制执行时机:将同步任务转为异步,避免频繁更新。
- 批量更新:合并同一事件循环内的多次数据变更,减少重复渲染。
- 优先级调度:确保任务按正确顺序执行(如父组件优先于子组件更新)。
通过调度器机制,Vue3 实现了高效且灵活的响应式更新策略。