Computed 的缓存机制是自动响应式闭环:惰性求值(不访问不执行)、依赖追踪(记录响应式字段变化)、缓存复用(dirty为false时直接返回旧值);非响应式数据如 Date.now() 不触发重算。

Computed 的缓存机制不是“手动记个结果”,而是一套自动运转的响应式闭环:它只在真正被读取时才开始算,算完就存着;只要它依赖的数据没变,下一次读就直接给旧值——不重跑、不浪费、不误判。
惰性求值:不访问,就不执行
computed 的 getter 函数默认不会在组件创建时运行。它被包装成一个 lazy effect,只有当你第一次访问 .value(比如在模板中写 `{{ countMsg }}`,或 JS 中读取 `countMsg.value`)时,Vue 才会执行这个函数,并把结果缓存起来。
这意味着:
- 即使依赖数据已经变了,只要没人读它,就不会触发重算,也不会标记为“脏”
- 适合开销大但未必用到的逻辑,比如处理万级列表、生成复杂配置对象
- 避免初始化阶段无意义的预计算,提升首屏性能
依赖追踪:知道“谁变了”才能决定“要不要重算”
首次执行 getter 时,Vue 会全程监听你读取了哪些响应式数据——比如 count.value、user.name、filters.active。这些路径会被记录下来,构成该 computed 的“依赖图”。
后续只要其中任意一个响应式字段触发 setter(如 count.value = 5),Vue 就立刻把对应 computed 的 dirty 标志设为 true,但不马上重算。
缓存复用:依赖未变,就绝不重复执行
每次读取 computed 的 .value 时,内部会检查 dirty 状态:
- 若 dirty === true:执行 getter,更新缓存值,再置 dirty = false
- 若 dirty === false:跳过计算,直接返回上一次的结果
所以模板里引用同一个 computed 十次,也只执行一次 getter;methods 则每次都会调用,毫无缓存。
为什么 Date.now() 或 Math.random() 不生效?
因为它们不是响应式数据,不在依赖图里。Vue 完全无法感知这些值是否变化,也就永远不会把 dirty 设为 true。结果就是:computed 第一次运行时取到的时间戳,之后永远不变——这不是缓存出错,而是根本没纳入响应式系统。
类似情况还包括:
- 普通 const 变量、非 reactive 对象的属性
- props 解构后未用 toRefs 包裹的值
- setTimeout 回调里读取的 ref(初始收集阶段捕获不到)


















