computed本质是lazy effect,创建时不执行也不收集依赖,首次读取.value时才运行并建立响应式依赖,后续变更仅标记dirty,下次读取时才重新计算并缓存结果。
计算属性(
computed
)在组合式 API 中并非简单封装的 getter,而是基于响应式系统核心——
Effect
构建的惰性、可缓存的依赖追踪视图。它的本质是一个“被包装过的响应式副作用”,其重计算时机、缓存策略和依赖收集逻辑,全部由
的调度机制与
实例行为决定。
computed 本质是一个 lazy effect
调用
时,Vue 并不立即执行函数,而是创建一个
实例,并将其
标志设为
:
该 effect 不会自动运行,也不在创建时收集依赖
只有当外界首次读取
时,才触发
,此时才真正执行 getter 并建立响应式依赖关系
后续依赖变化时,effect 不会自动重新运行,而是标记自身为“脏”(
),等待下一次
访问时才重新求值
依赖收集发生在第一次 .value 读取时
computed 内部持有一个
(即 lazy effect 的执行函数),并在其
访问器中做如下关键操作:
若
,则调用
执行 getter,此时
开始工作,将当前 activeEffect(即该 computed 的 effect)加入所访问响应式属性(如
)的依赖列表
执行结果被缓存到
,
置为
若依赖未变,后续读取直接返回缓存值,跳过 getter 执行
依赖变化时仅标记为脏,不立即更新
当
被修改,触发其
流程时:
会遍历所有依赖于
的 effects,对其中是 computed effect 的,只执行
computed 的 scheduler 就是
,不做重计算,只标记自己失效
这保证了 computed 的惰性:多次依赖变更只标记一次脏,直到下次读取才合并计算,避免冗余执行
ref 接口背后是 ReactiveEffect + 响应式缓存
computed 返回的是一个
实例,它同时实现了
和
行为:
是响应式访问器:读取时触发依赖收集或缓存返回;写入(仅可写 computed)时调用 setter 并触发 trigger
内部
缓存与
标志协同,实现“有需才算”的语义
整个生命周期完全复用
的 track/trigger/scheduler 三件套,没有额外的响应式层
不复杂但容易忽略:computed 不是“监听器”,也不是“自动刷新变量”,它是 effect 的一种受控使用方式——用 lazy + scheduler + 缓存,把响应式求值变成按需调用的纯函数接口。
effectReactiveEffectcomputed(() => x.value + y.value)ReactiveEffectlazytruecomputedRef.valueeffect.run()dirty = true.valuerunnerget valuedirty === truerunner()trackx.value_valuedirtyfalsex.valuetriggerxeffect.scheduler() => (dirty = true)ComputedRefImplRefGetter.value_valuedirtyeffect