Vue3 的响应式系统源码级 computed 的实现原理与缓存机制
字数 1251 2025-12-09 23:09:33
Vue3 的响应式系统源码级 computed 的实现原理与缓存机制
描述:
Vue3 的 computed 是计算属性 API,它会根据其依赖的响应式数据自动计算值,并缓存计算结果,只有在依赖变化时才重新计算。理解其实现原理需要深入分析 effect 系统、缓存机制和依赖收集过程。
解题过程:
1. computed 的基本概念
- 计算属性是基于其他响应式状态计算出的值
- 具有惰性求值特性:只有实际访问时才计算
- 具有缓存机制:依赖未变化时直接返回缓存值
- 返回值是一个响应式的 ref 对象,可以通过
.value访问
2. computed 函数的基本结构
在 Vue3 源码中,computed 函数接收一个 getter 函数或包含 get/set 的对象:
// reactivity/computed.ts
export function computed<T>(
getterOrOptions: ComputedGetter<T> | WritableComputedOptions<T>
) {
let getter: ComputedGetter<T>
let setter: ComputedSetter<T>
// 判断参数类型
if (isFunction(getterOrOptions)) {
getter = getterOrOptions
setter = NOOP // 只读计算属性
} else {
getter = getterOrOptions.get
setter = getterOrOptions.set || NOOP
}
return new ComputedRefImpl(
getter,
setter,
isFunction(getterOrOptions) || !getterOrOptions.set
) as any
}
3. ComputedRefImpl 核心类实现
这是 computed 的核心类,实现了计算逻辑和缓存机制:
class ComputedRefImpl<T> {
// 响应式相关的属性
public dep?: Dep
private _value!: T
private _dirty = true // 脏标记,表示是否需要重新计算
public readonly effect: ReactiveEffect<T>
public readonly __v_isRef = true
public readonly [ReactiveFlags.IS_READONLY]: boolean
constructor(
getter: ComputedGetter<T>,
private readonly _setter: ComputedSetter<T>,
isReadonly: boolean
) {
// 创建 effect,但不立即执行
this.effect = new ReactiveEffect(
getter,
() => {
// 调度器函数,当依赖变化时被调用
if (!this._dirty) {
this._dirty = true
// 触发依赖此 computed 的 effect
triggerRefValue(this)
}
}
)
this.effect.computed = this
this[ReactiveFlags.IS_READONLY] = isReadonly
}
get value() {
// 收集依赖
trackRefValue(this)
// 如果脏标记为 true,需要重新计算
if (this._dirty) {
this._dirty = false
// 执行 effect 获取新值
this._value = this.effect.run()!
}
return this._value
}
set value(newValue: T) {
this._setter(newValue)
}
}
4. 缓存机制的实现原理
计算属性的缓存机制通过以下方式实现:
脏标记 (_dirty) 机制:
_dirty初始为true,表示需要计算- 当访问
value时,如果_dirty为true,则执行计算,然后设为false - 当计算属性的依赖发生变化时,调度器函数会将
_dirty设为true
依赖收集与触发流程:
// 访问 computed.value 时的流程:
1. trackRefValue(this) // 收集当前 activeEffect 对 computed 的依赖
2. 检查 _dirty
- 如果为 true:
a. 执行 this.effect.run() // 计算新值
b. 在计算过程中,会收集 computed 内部对响应式数据的依赖
c. 将 _dirty 设为 false
d. 缓存计算结果到 _value
- 如果为 false:
直接返回缓存的 _value
3. 返回 _value
// 当依赖变化时的流程:
1. 依赖的响应式数据变化,触发 computed 的调度器函数
2. 调度器函数将 _dirty 设为 true
3. 触发所有依赖此 computed 的 effect
5. 惰性计算机制
- 计算属性不会在创建时立即计算
- 只有在实际访问
.value时才进行计算 - 这是通过不在构造函数中执行 effect,而是在 getter 中按需执行实现的
6. 只读 vs 可写计算属性
// 只读计算属性
const readOnlyComputed = computed(() => count.value * 2)
// 可写计算属性
const writableComputed = computed({
get: () => count.value * 2,
set: (val) => { count.value = val / 2 }
})
可写计算属性通过 _setter 函数实现,在设置值时调用用户定义的 setter 函数。
7. 依赖关系图
计算属性建立了两层依赖关系:
外部 effect → computed (通过 trackRefValue)
computed → 内部依赖 (通过 computed 的 effect)
当内部依赖变化时:
- 触发 computed 的调度器
- 调度器将
_dirty设为 true - 触发外部 effect
- 外部 effect 重新执行时,再次访问 computed.value
- 此时
_dirty为 true,重新计算值
8. 性能优化点
- 避免重复计算:只要依赖不变化,多次访问都返回缓存值
- 懒计算:不访问就不计算
- 惰性更新:依赖变化时不立即计算,等到访问时才计算
- 精确更新:只有当依赖变化且计算属性被使用时才重新计算
9. 与普通 effect 的区别
- 懒执行:computed 不会自动运行,需要访问才执行
- 有缓存:依赖不变时直接返回缓存值
- 返回值:computed 返回一个 ref 对象,effect 不返回值
- 调度机制:computed 有特殊的调度器处理依赖变化
总结:
Vue3 的 computed 通过 ComputedRefImpl 类实现,核心是脏标记机制和两层依赖收集。它结合了 effect 系统的响应式能力和智能缓存机制,既保证了响应式更新,又避免了不必要的重复计算,是 Vue3 响应式系统中性能优化的重要体现。