computed 缓存本质是按需重算,依赖响应式数据变更信号,未读取的字段变化不触发重算;失灵主因是依赖未被Vue捕获,如普通对象、新增属性未响应、调用不可追踪函数等。

computed 的缓存机制本质是“按需重算”:它不靠定时刷新或手动清空,而是严格依赖响应式数据的变更信号。只要它读取过的响应式字段没变,哪怕组件渲染十次、模板里调用五次,也只执行一次计算,其余全部返回缓存值。
依赖追踪决定是否重算
Vue 在 computed 首次求值时,会实时记录所有被访问的响应式来源(比如 ref.value、reactive 对象的属性、其他 computed)。这个过程全自动,无需手动声明。
- 修改了 firstName → fullName 重算;改了 age → fullName 完全不动
- 分支逻辑中只走 if 分支 → 只收集 if 内访问的依赖;后续 condition 变化导致走 else 分支 → 依赖自动切换,缓存自然失效
- 没读到的字段,无论是否变化,都不会触发重算
缓存不是万能的:哪些操作会让它“失灵”
缓存失效通常不是机制坏了,而是依赖没被 Vue 捕获到。常见情况包括:
- 把普通对象(非 reactive / ref)直接用于计算逻辑,Vue 无法监听其变化
- 对 reactive 对象新增属性但没用 Vue.set 或 ref 包裹,新字段不响应
- 在 computed 里调用 Date.now()、Math.random()、this.$refs 或发起请求——这些无法追踪,结果可能“假缓存”
- 用 Object.assign({}, obj) 或展开整个大型响应式对象做参数,既触发多余依赖收集,又让 key 不稳定
怎么让缓存真正起作用
缓存价值要靠写法配合。关键不是“有没有”,而是“能不能稳稳抓住依赖”:
- 昂贵计算尽量拆成多个小 computed,比如先 filteredList,再 sortedList,各自独立缓存,局部更新不影响全局
- 模板中别写 {{ items.filter(x => x.active).map(x => x.name).join(', ') }},提取为 computed 更清晰、更高效
- 需要传参的场景保留 methods,但它内部可复用已缓存的 computed 结果,避免重复处理原始数据
- 真有高开销且输入多变(如深度遍历、正则匹配),可在 setup 中用 Map 手动缓存,key 仅取必要字段拼接,不用整个响应式对象
缓存本身很简单,难的是写得让它“认得清依赖、守得住边界”。写对了,性能提升是自然发生的。











