Vue3 的响应式系统源码级 effect 的 lazy 选项与手动触发机制原理
字数 914 2025-11-29 13:55:40
Vue3 的响应式系统源码级 effect 的 lazy 选项与手动触发机制原理
描述
effect 的 lazy 选项是 Vue3 响应式系统中一个重要的性能优化特性。当设置 lazy: true 时,effect 不会在创建时立即执行,而是需要手动触发。这个机制在 computed 属性、watch 等高级功能中有着关键作用,能够避免不必要的初始计算和副作用执行。
核心原理详解
1. effect 函数的基本结构
function effect(fn, options = {}) {
// 创建副作用函数
const effectFn = () => {
cleanup(effectFn) // 清理旧依赖
activeEffect = effectFn
effectStack.push(effectFn)
const res = fn() // 执行原始函数
effectStack.pop()
activeEffect = effectStack[effectStack.length - 1]
return res
}
// 设置选项
effectFn.options = options
effectFn.deps = [] // 依赖集合
// 非 lazy 时立即执行
if (!options.lazy) {
effectFn()
}
return effectFn // 返回可手动调用的函数
}
2. lazy 选项的工作机制
- 默认行为(lazy: false):effect 创建后立即执行副作用函数,建立依赖关系
- 延迟执行(lazy: true):effect 只创建但不执行,返回可手动调用的函数
// 立即执行的 effect
effect(() => {
console.log('立即执行:', state.count)
})
// 延迟执行的 effect
const lazyEffect = effect(() => {
console.log('延迟执行:', state.count)
}, { lazy: true })
// 需要时手动触发
lazyEffect()
3. 手动触发机制的实现
effect 函数返回的是一个可执行的函数,这个函数具有完整的响应式能力:
const runner = effect(
() => state.count + 1,
{ lazy: true }
)
// 手动执行并获取返回值
const value = runner() // 执行副作用并返回计算结果
console.log(value) // 输出:当前 count + 1 的值
4. 在 computed 中的具体应用
computed 属性正是基于 lazy effect 实现的:
function computed(getter) {
let value
let dirty = true // 脏检查标志
const effectFn = effect(getter, {
lazy: true,
scheduler() {
dirty = true // 依赖变化时标记为脏
trigger(obj, 'value') // 触发更新
}
})
const obj = {
get value() {
if (dirty) {
value = effectFn() // 手动执行获取新值
dirty = false // 重置脏检查
track(obj, 'value') // 收集依赖
}
return value
}
}
return obj
}
5. 执行流程的详细分析
步骤1:创建 lazy effect
const lazyRunner = effect(() => {
return state.a + state.b
}, { lazy: true })
- 创建 effectFn 但不会执行
- 设置 lazy 选项为 true
- 返回 effectFn 函数供手动调用
步骤2:手动触发执行
const result = lazyRunner()
- 执行 cleanup 清理旧依赖
- 设置 activeEffect 为当前 effectFn
- 执行原始函数,访问响应式数据
- 触发 track 依赖收集
- 返回计算结果
步骤3:依赖更新时的重新执行
当 state.a 或 state.b 变化时:
- 触发 effectFn 的 scheduler(如果有)
- 或者直接重新执行 effectFn
- 重新计算并返回新值
6. 性能优化优势
避免不必要的初始计算:
// 如果没有 lazy 选项,这个复杂计算会立即执行
const expensiveComputed = computed(() => {
return heavyCalculation(state.data)
})
// 只有实际访问时才计算
console.log(expensiveComputed.value)
精确控制执行时机:
const runner = effect(() => {
// 只在特定条件下执行
if (state.needUpdate) {
updateDOM()
}
}, { lazy: true })
// 在合适的时机手动触发
state.needUpdate = true
runner()
7. 与调度器的协同工作
lazy effect 可以与调度器结合,实现更精细的控制:
const queuedEffect = effect(() => {
console.log('批量更新:', state.count)
}, {
lazy: true,
scheduler: (job) => {
// 将任务加入微任务队列
queueMicrotask(job)
}
})
// 手动触发,但会进入调度器
queuedEffect()
总结
effect 的 lazy 选项通过延迟执行和手动触发机制,为 Vue3 的响应式系统提供了重要的性能优化手段。这种设计模式使得 computed 属性、watch 监听器等高级功能能够实现按需计算和精确控制,避免了不必要的副作用执行,提升了应用的整体性能。