广瀚云办公
55.99MB · 2025-10-09
在 Vue 3 的响应式系统中,computed
是一个非常重要的功能,它用于创建基于依赖自动更新的计算属性。本文将通过分析源码,理解 computed
的底层实现逻辑,帮助你从源码层面掌握它的原理。
computed
的基本使用在使用层面上,computed
有两种常见用法:
const count = ref(1)
const plusOne = computed(() => count.value + 1)
console.log(plusOne.value) // 输出 2
plusOne.value++ // 报错,因只读
const count = ref(1)
const plusOne = computed({
get: () => count.value + 1,
set: (val) => { count.value = val - 1 }
})
plusOne.value = 3
console.log(count.value) // 输出 2
这两种形式在底层源码中都会通过一个统一的类 ComputedRefImpl
来实现。
ComputedRefImpl
ComputedRefImpl
是 Vue 3 中计算属性的核心实现类,它实现了响应式依赖追踪和懒执行机制。
export class ComputedRefImpl<T = any> implements Subscriber {
_value: any = undefined // 缓存计算后的值
dep: Dep = new Dep(this) // 依赖收集容器
readonly __v_isRef = true // 标记为 ref 类型
readonly __v_isReadonly: boolean // 是否只读
deps?: Link // 依赖链表(订阅者)
flags: EffectFlags = EffectFlags.DIRTY // 标志位,初始为脏值
globalVersion: number = globalVersion - 1 // 全局版本号
isSSR: boolean // 是否在服务端渲染环境中
next?: Subscriber = undefined // 下一个订阅者
// 调试钩子
onTrack?: (event: DebuggerEvent) => void
onTrigger?: (event: DebuggerEvent) => void
constructor(
public fn: ComputedGetter<T>,
private readonly setter: ComputedSetter<T> | undefined,
isSSR: boolean,
) {
this[ReactiveFlags.IS_READONLY] = !setter
this.isSSR = isSSR
}
}
关键点:
flags
:使用 EffectFlags
管理状态,如 DIRTY
表示需要重新计算。dep
:内部维护依赖列表,供其他响应式对象追踪。_value
:缓存上次计算的值,实现懒计算。__v_isReadonly
:若没有传入 setter,则为只读计算属性。value
的访问逻辑get value(): T {
const link = this.dep.track() // 依赖追踪
refreshComputed(this) // 若脏则重新计算
if (link) {
link.version = this.dep.version
}
return this._value
}
这里是 computed
的核心机制:
dep.track()
让当前计算属性被其他响应式数据订阅。.value
时,才会执行 getter 重新计算。link.version
确保依赖的版本号一致,以判断是否需要重新计算。value
的设置逻辑set value(newValue) {
if (this.setter) {
this.setter(newValue)
} else if (__DEV__) {
warn('Write operation failed: computed value is readonly')
}
}
setter
,则允许外部修改计算属性值;当依赖的响应式数据发生变化时,notify()
会被调用:
notify(): true | void {
this.flags |= EffectFlags.DIRTY // 标记为脏值
if (!(this.flags & EffectFlags.NOTIFIED) && activeSub !== this) {
batch(this, true) // 批量更新依赖
return true
}
}
这里的关键是:
batch
批量更新,避免重复通知。computed
函数入口外层的 computed
函数是一个工厂方法:
export function computed<T>(
getterOrOptions: ComputedGetter<T> | WritableComputedOptions<T>,
debugOptions?: DebuggerOptions,
isSSR = false,
) {
let getter: ComputedGetter<T>
let setter: ComputedSetter<T> | undefined
if (isFunction(getterOrOptions)) {
getter = getterOrOptions
} else {
getter = getterOrOptions.get
setter = getterOrOptions.set
}
const cRef = new ComputedRefImpl(getter, setter, isSSR)
if (__DEV__ && debugOptions && !isSSR) {
cRef.onTrack = debugOptions.onTrack
cRef.onTrigger = debugOptions.onTrigger
}
return cRef as any
}
这里做了两件事:
ComputedRefImpl
实例;onTrack
、onTrigger
)。Vue 3 中的 computed
实现基于以下关键机制:
机制 | 作用 |
---|---|
惰性求值 (Lazy Evaluation) | 仅在访问时计算结果 |
缓存结果 (Caching) | 若依赖未变则返回缓存值 |
依赖追踪 (Dependency Tracking) | 自动感知依赖变化 |
脏标记 (Dirty Flag) | 控制何时重新计算 |
批量更新 (Batching) | 提高性能,减少重复通知 |
从源码可以看到,computed
实际上是一个特殊的 ref
,但拥有更多的依赖追踪与缓存机制,是 Vue 响应式系统中非常精妙的一环。
本文内容由人工智能生成,仅供学习与参考使用,请在实际应用中结合自身情况进行判断。