vue.js中computed通过惰性响应式effect实现自动缓存,依赖响应式数据读取建立追踪,修改时标记dirty并按需重算,而非手动缓存逻辑。

Vue.js 中缓存副作用函数的计算结果,核心不是“手动缓存”,而是通过 computed 计算属性 这一机制,把副作用函数(即 getter 函数)封装为一个惰性、可追踪、带 dirty 标志的响应式 effect,从而实现自动、精准、按需的缓存。
它不依赖开发者写 if (!cache) cache = ... 这类逻辑,而是由响应式系统在运行时动态决定:什么时候该算、什么时候直接返回缓存值。
computed 本质是一个带缓存的副作用函数
computed(() => x + y) 创建的不是一个普通函数,而是一个特殊 effect:
- 被标记为
lazy: true和computed: true - 初始化时不执行,只保存 getter
- 首次读取时才执行 getter,并在执行过程中自动收集依赖(比如
x、y) - 执行结果被缓存在
effect._value,同时dirty = false - 后续读取直接返回
_value,跳过重新执行
const count = ref(1) const doubled = computed(() => count.value * 2) console.log(doubled.value) // → 2(首次执行,收集 count 为依赖) count.value = 3 console.log(doubled.value) // → 6(count 变更触发 trigger → mark dirty → 下次读取重算)
缓存生效的关键条件:依赖必须是响应式且被实际读取
只有在 getter 中真正访问了响应式数据的属性,才会建立依赖连接。例如:
将一个或多个视频上传至YouTube。当用户想“上传到YouTube”“发YouTube”“批量上传”“upload to YouTube”“post videos to YouTube”或发布到YouTube时使用此技能。
- ✅
this.list.filter(...)→ 会追踪list的变化 - ✅
this.user.name→ 追踪user.name - ❌
const { name } = this.user; return name→ 解构后name是普通变量,不响应 - ❌
this.items[this.activeKey]→ 若activeKey不变,items整体替换可能漏触发(因 Proxy 无法监听数组/对象整体替换)
所以缓存是否生效,取决于你怎么写 getter,而不是 computed 本身“失灵”。
哪些操作会让缓存失效或失效不可控
- 在 getter 中调用无响应式的外部函数(如
Math.random()、Date.now()),它们不参与依赖追踪,但会污染结果稳定性 - 修改响应式数据发生在异步回调中(如
setTimeout(() => state.x = 1)),只要修改的是响应式字段,依然能触发更新 —— 但要注意:异步本身不是依赖,修改动作才是 - 使用
markRaw或shallowRef包裹的数据不会被深度代理,其嵌套属性变更不会触发依赖更新 - 直接替换整个响应式对象(如
obj = reactive({}))会丢失原有依赖关系,应改用Object.assign(obj, {...})或逐个赋值
想手动控制缓存?优先考虑 watchEffect + 状态变量
如果逻辑复杂、含异步、或需要“主动触发重算”,computed 就不太合适。此时更适合:
- 用
ref存储结果 - 用
watchEffect自动追踪依赖并更新 ref - 必要时调用
watchEffect.resume()或手动重置状态
const result = ref(null)
const timer = ref(null)
watchEffect((onInvalidate) => {
onInvalidate(() => clearTimeout(timer.value))
timer.value = setTimeout(() => {
result.value = expensiveAsyncCalc(props.id)
}, 100)
})
这种方式比硬塞异步进 computed 更清晰、可控,也保留了依赖追踪能力。
缓存不是加个 memo 就完事,而是整个响应式调度链路协同工作的结果:track → trigger → scheduler → lazy re-evaluation。写好 getter,依赖自然被收;改对响应式数据,缓存自然被破。
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!










