Q13
★ ★ ★ ★ ★
Vue 3 的 computed 是如何实现的?
⚡ 速记答案(30 秒)
本质是一个带缓存(lazy)的 effect:首次访问或依赖变化时才执行 getter。内部用 dirty 标记 + 缓存值:依赖触发时只把 dirty 设为 true;下次访问时才重新计算。
📖 详细讲解
标准面试回答(推荐记住)
computed 本质是一个惰性的 effect。内部维护 dirty 标记和缓存值,首次访问或依赖变化时才执行 getter。当依赖变化时,只是把 dirty 设为 true,并不立即计算;下次读取 computed 值时才真正重新计算,这样实现了缓存和按需更新。
实现细节
ComputedRefImpl
内部包含一个 ReactiveEffect,但配置了 lazy: true 和自定义的 scheduler。
执行流程:
- 初始化:创建 Effect,但因为 lazy 不会立即执行。dirty 设为 true。
- 首次读取:触发 Getter -> 检测到 dirty 为 true -> 执行 Effect.run() -> 计算新值并缓存 -> dirty 设为 false -> 收集依赖(track)。
- 依赖更新:触发 Trigger -> 执行 Scheduler -> 将 dirty 设为 true -> 触发 computed 自身的 Trigger(通知依赖这一计算属性的效果)。
- 再次读取:检测到 dirty 为 false -> 直接返回缓存值。
这种机制保证了只有在依赖变化且被读取时才会重新计算,避免无效计算。
✅ 面试要点
- •理解惰性计算和缓存机制
- •知道 dirty 标记的作用
- •能解释 computed 的性能优势
💻 代码示例
computed 原理简化
// computed 简化实现
function computed<T>(getter: () => T) {
let value: T
let dirty = true // 脏标记
// 创建 effect,但不立即执行
const effect = new ReactiveEffect(getter, () => {
// 依赖变化时的调度器
if (!dirty) {
dirty = true
// 触发依赖此 computed 的更新
trigger(computedRef, 'value')
}
})
const computedRef = {
get value() {
if (dirty) {
value = effect.run()
dirty = false
}
return value
}
}
return computedRef
}