vue 3 的 computed 缓存机制本质是依赖追踪、懒执行与脏检查协同:首次访问执行 getter 并缓存结果,依赖变更时标记 _dirty = true,下次访问才重新计算;只追踪实际读取的响应式属性,对象 ref 修改属性值不触发更新,需改变引用或使用 reactive。

Vue 3 中 computed 的缓存机制,本质是“依赖追踪 + 懒执行 + 脏检查”的协同结果,不是简单地把上一次返回值存起来就完事。
依赖追踪:只关心真正读取的属性
当定义一个 computed 时,Vue 会用 effect 包裹它的 getter 函数,并在首次执行过程中,通过 Proxy 的 get 拦截器自动收集所有被访问的响应式属性(比如 ref.value、reactive.obj.name)。这些访问路径构成它的依赖图。
关键点在于:它只追踪「实际被读取」的属性。例如:
-
state = ref({ a: 1, b: 2 }),而computed(() => state.value.a)只依赖state.value.a; - 修改
state.value.b不会触发重新计算; - 但若改成
state.value = { ...state.value, a: 2 },整个引用变更,依赖图失效,就会重新求值。
懒执行与缓存值:不访问就不算,算了就存着
computed 是惰性求值的。它内部维护一个 _dirty 标志和一个 _value 缓存字段:
- 首次访问
xxx.value时,才执行 getter,得到结果并缓存到_value,同时标记_dirty = false; - 后续访问直接返回
_value,跳过函数执行; - 只有当某个依赖项变化(通过
trigger触发),才会把_dirty设为true; - 下一次访问时,发现
_dirty === true,才重新执行 getter 并更新缓存。
对象类型 ref 的陷阱:缓存失效常因依赖未正确变更
这是最容易出错的地方。看这个例子:
const obj = ref({ count: 0 })const msg = computed(() => `count: ${obj.value.count}`)
如果写 obj.value.count++,虽然值变了,但 computed 不会更新——因为 obj 的引用没变,且 obj.value.count 的读取路径在依赖收集阶段已固定,Vue 并未监听到该属性的变更通知(Proxy 的 set 拦截确实触发了,但依赖收集发生在 get 阶段,set 不主动重收集)。
正确做法是让引用变化,比如:obj.value = { ...obj.value, count: obj.value.count + 1 },或改用 reactive 配合深层响应式逻辑。
与 methods 的根本区别:缓存有状态,methods 没状态
methods 是纯函数调用,每次渲染都执行;computed 是响应式数据节点,自带生命周期(创建 → 收集依赖 → 缓存 → 标记脏 → 重算 → 再缓存)。它的缓存不是靠变量保存,而是靠 effect 实例的 _dirty 控制流 + 闭包内的 _value 存储实现的。
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!











