computed 通过依赖追踪实现缓存,仅在响应式依赖变化时重新计算,避免重复执行;普通 methods 无缓存、每次调用都执行;缓存失效主因是依赖未被正确捕获,如对象整体替换或新增非响应式属性。

因为 computed 不是每次调用都重新执行,而是只在依赖的响应式数据真正变化时才重新计算,其余时候直接返回缓存结果——这个机制大幅减少了重复开销,尤其在模板频繁读取、大数据处理或复杂逻辑场景下,性能优势非常明显。
缓存怎么工作的?关键在依赖追踪
computed 在第一次求值时,会“路过”所有被读取的响应式字段(比如 ref、reactive 对象属性、其他 computed),Vue 内部自动记录下这些依赖路径。之后只要这些字段没触发 setter(即值未变),哪怕组件重渲染十次,computed 也不重跑。
- 改了 firstName → fullName 重算;改了 age → fullName 完全不受影响
- 如果函数里用了 Date.now() 或 this.$refs,这些不会被追踪,缓存可能“误判”,结果不更新
- 异步回调里的响应式读取(如 setTimeout(() => console.log(this.count), 100))也不会进依赖图,别指望它触发更新
和普通函数(或 methods)比,差在哪?
普通函数没有 Vue 的响应式上下文,每次调用都是全新执行;methods 虽在 Vue 实例上,但依然无缓存、无依赖感知。
- 模板中写 {{ getFullName() }}:每次 re-render 都调用,字符串拼接、三元判断重复跑
- 模板中写 {{ fullName }}:首次计算后,只要 firstName/lastName 不变,永远拿缓存
- 同一个 computed 在模板里用 5 次,也只算 1 次;methods 用 5 次就执行 5 次
什么情况下缓存会“失效”或“不准”?
不是缓存坏了,而是依赖没被正确捕获。常见坑点:
- 对响应式对象做了整体替换:this.user = { ...this.user, avatar: newUrl } → 新对象丢失响应性,应改用 Vue.set 或 reactive + ...spread 配合 proxy 语义
- 给 reactive 对象新增属性没用 Vue.set 或 ref 包裹 → 新增字段不响应,自然不触发重算
- 在 computed 里直接修改响应式数据(如 this.list.push(x))→ 这既是副作用,也破坏缓存前提(纯函数要求)
实战中怎么放大缓存价值?
缓存不是摆设,要主动配合使用方式来释放性能红利:
- 把大计算拆成多个小 computed:比如先 filteredList,再 sortedList,最后 summary —— 各自独立缓存,局部更新不影响全局
- 模板中避免 {{ items.filter(...).map(...).join(', ') }} 这类内联表达式,提取为 computed 更清晰也更高效
- 需要参数的场景保留 methods,但可让 methods 内部调用已缓存的 computed 做基础处理,减少重复逻辑


















