vue 3 computed
1. 类结构定义
class ComputedRefImpl<T> {
// 核心状态
private _value!: T // 缓存的计算结果
private _dirty = true // 脏值标志
public readonly effect: ReactiveEffect<T>
// 是否支持设置(只读判断)
public readonly __v_isReadonly: boolean
constructor(
getter: () => T,
private readonly _setter: (newValue: T) => void,
isReadonly: boolean
) {
// 创建一个 effect 并配置调度器
this.effect = new ReactiveEffect(getter, () => {
if (!this._dirty) {
this._dirty = true // 依赖变化时标记为需要重新计算
trigger(toRaw(this), TriggerOpTypes.SET, 'value') // 触发依赖更新
}
})
this.effect.computed = this // 关联 effect 和 computed 实例
this.__v_isReadonly = isReadonly
}
get value(): T {
// 关键:触发依赖收集和重新计算
if (this._dirty) {
this._value = this.effect.run()
this._dirty = false
}
track(toRaw(this), TrackOpTypes.GET, 'value') // 收集当前依赖(如组件渲染函数)
return this._value
}
set value(newValue: T) {
this._setter(newValue) // 调用用户提供的 setter
}
}
2. 关键实现细节
(1) 依赖追踪机制
- 计算属性本身是被观察者:当其他代码(如模板)访问
computed.value时,会触发track收集对computedRef的依赖。 - 计算属性内部是观察者:通过
this.effect订阅其getter中使用的响应式数据的变化。
(2) 调度器(Scheduler)
() => {
if (!this._dirty) {
this._dirty = true
trigger(toRaw(this), TriggerOpTypes.SET, 'value')
}
}
- 当计算属性依赖的响应式数据变化时,调度器会被触发:
- 标记
_dirty = true:表示下次访问.value时需要重新计算。 - 调用
trigger:通知依赖此计算属性的副作用(如组件重新渲染)更新。
- 标记
(3) 懒计算(Lazy Evaluation)
- 计算属性的求值延后到 真正访问
.value时进行:if (this._dirty) { // 只有当依赖变化时才会重新计算 this._value = this.effect.run() // 执行 getter this._dirty = false }
3. 使用示例与流程
(1) 创建计算属性
const count = ref(0)
const double = computed(() => count.value * 2)
过程:
- 实例化
ComputedRefImpl - 创建
ReactiveEffect包裹 getter,并配置调度器函数
(2) 访问计算属性(首次)
console.log(double.value) // 0
流程:
_dirty为true→ 执行this.effect.run()- 运行
() => count.value * 2得到 0 - 收集
count.value到此effect的依赖中 _dirty标记为false- 触发
track,将当前渲染函数等副作用订阅到double的变化
(3) 依赖更新
count.value++
流程:
count变更触发double的effect的调度器- 标记
_dirty = true - 调用
trigger通知订阅者(如组件重新渲染)
(4) 再次访问
console.log(double.value) // 2 (触发重新计算)
4. 性能优化手段
- 缓存机制:重复访问
.value但依赖未变化时直接返回_value。 - 批量更新:若多次修改依赖值,最终只重新计算一次。
- 无效计算规避:若计算属性未被用到,其内部
effect不会被激活。
与模板的联动
当模板中使用计算属性时:
<template>{{ double }}</template>
- 组件渲染时会访问
double.value→ 触发getter - 模板成为计算属性的订阅者,当
count变化时:- 调度器标记
_dirty = true trigger触发组件重新渲染- 渲染时再次访问
double.value→ 重新计算并缓存
- 调度器标记
源码中的实际调用
当调用 computed() 函数时:
function computed<T>(
getter: () => T,
debugOptions?: DebuggerOptions
): ComputedRef<T>
function computed<T>(
options: WritableComputedOptions<T>,
debugOptions?: DebuggerOptions
): WritableComputedRef<T>
function computed<T>(
getterOrOptions: any,
debugOptions?: DebuggerOptions
) {
let getter: () => T
let setter: (v: T) => void
// 处理 getter/setter 参数
if (isFunction(getterOrOptions)) {
getter = getterOrOptions
setter = __DEV__ ? () => { /* 警告 */ } : NOOP
} else {
getter = getterOrOptions.get
setter = getterOrOptions.set
}
// 创建 ComputedRefImpl 实例
return new ComputedRefImpl(
getter,
setter,
isFunction(getterOrOptions) || !getterOrOptions.set
) as any
}
总结
ComputedRefImpl 通过组合以下特性实现计算属性:
- 响应式 Effect 系统:追踪
getter中的依赖。 - 脏值标记(_dirty):优化重新计算时机。
- 缓存机制(_value):避免重复计算。
- 调度器(Scheduler):控制依赖变更时的触发逻辑。
- 懒求值(Lazy):不访问时不会运行计算。
这种设计保证了计算属性既高效响应依赖变化,又避免不必要的性能消耗。

浙公网安备 33010602011771号