computed 依赖追踪生效的关键是 getter 必须为纯函数:只读响应式数据、不修改状态、不同步异步或副作用操作;否则依赖链断裂、缓存失效、更新异常。

要让 computed 的依赖追踪真正生效,关键是在 getter 内部写一个**纯函数**:只读响应式数据、不修改任何状态、不触发异步或外部操作。Vue 的依赖收集机制(基于 Proxy 的 get 拦截)只在函数执行过程中“看到”哪些响应式字段被访问了——一旦函数里掺杂副作用,依赖链就断了,缓存失效,甚至引发更新异常。
只读响应式字段,不碰非响应式值
Vue 只能追踪 ref、reactive、props、其他 computed 这些明确声明的响应式来源。如果读取了普通对象属性、全局变量、Date.now()、Math.random() 或通过 this._data 等非标准方式访问,依赖就无法建立。
- ✅ 正确:
computed(() => user.value.name + ' - ' + status.value) - ❌ 错误:
computed(() => obj.name + new Date().toISOString())(obj非响应式,Date无响应) - ? 补救:把时间戳转为响应式,例如用
const now = ref(Date.now()),再配合watchEffect定期更新
禁止在 getter 中修改状态
计算属性是“派生值”,不是“动作入口”。任何赋值操作(如 count.value++、list.push(item)、state.flag = true)都会污染依赖关系,可能导致无限循环或跳过更新。
- ❌ 错误示例:
computed(() => { userInfo.value.lastAccess = Date.now(); return userInfo.value }) - ✅ 替代方案:把状态变更移至
watch、methods或事件回调中;计算属性只负责“读”和“算”
不执行异步、DOM 操作或副作用函数
getter 必须同步返回结果。调用 fetch、setTimeout、console.log(调试除外)、$emit、document.querySelector 等,不仅破坏纯函数性,还会导致:
- 多次渲染时重复发起请求
- 缓存失效(因为 Vue 无法判断“这次结果是否还有效”)
- Devtools 依赖图显示不全或中断
需要异步逻辑?用 ref 存状态,watch 触发加载,再用另一个 computed 基于该 ref 做条件展示。
拆分中间态,让每层依赖清晰可缓存
复杂逻辑不要堆在一个 computed 里。每拆出一个独立 computed,就等于多一道缓存屏障。Vue 能分别追踪每一层的输入,确保只有真正变化的部分重算。
- 比如处理搜索列表:先
filtered(依赖list和query),再sorted(依赖filtered和sortKey),最后paged(依赖sorted和page) - 这样,仅
query改变时,只有filtered重算,sorted和paged直接复用











