computed执行顺序由依赖图拓扑关系决定,而非代码顺序;惰性求值,首次访问时按需深度优先计算;循环依赖会被拦截报错;求值时机取决于实际访问顺序和响应式依赖流向。

多个 computed 互相依赖时,执行顺序由依赖图的拓扑关系决定,而不是代码书写顺序。Vue 3 会自动解析它们之间的响应式依赖链,并确保:被依赖的 computed 先求值,依赖它的 computed 后求值。
依赖关系决定求值时机
computed 是惰性求值的。它不会在定义时立刻执行,而是在首次被读取(访问 .value)或在模板中首次使用时才触发计算。如果 A 依赖 B,B 依赖 C,那么第一次访问 A 时:
- C 先计算并缓存结果
- 接着 B 基于 C 的最新值计算并缓存
- 最后 A 基于 B 的最新值计算
这个过程是深度优先、按需触发的,不是 setup 执行时就“跑一遍”。
循环依赖会被 Vue 拦截并报错
如果你写成 A 依赖 B,B 又反过来依赖 A(直接或间接),Vue 3 在首次求值时会检测到循环依赖,并抛出类似 ComputedRefImpl is already dirty 或 Maximum call stack size exceeded 的错误。这不是执行顺序问题,而是逻辑错误,必须打破循环。
立即学习“前端免费学习笔记(深入)”;
常见诱因包括:
- 在 getter 中修改了另一个 computed 所依赖的响应式源(比如给 ref 赋值)
- 两个 computed 通过共享的 ref 或 reactive 对象形成隐式双向依赖
- 在 getter 内部调用了一个会触发自身重新求值的方法(如意外触发 watch 或副作用)
没有“注册顺序”,只有“求值时的依赖流”
你不能靠把 computedA 写在 computedB 上面,就让 A 一定先于 B 执行。setup 中的声明只是注册,不触发计算。真正影响顺序的是:
- 谁在模板里/代码中先被访问(如 {{ a }} 渲染早于 {{ b }})
- 谁被哪个 watch 或 onMounted 里的逻辑先读取
- 它们之间是否存在明确的响应式数据流向(即 getter 内部是否读取了另一个 computed.value)
例如:
const b = computed(() => a.value + 1)
const a = computed(() => 42)
虽然 b 写在 a 前面,但访问 b 时,a 会自动先求值——因为 b 显式依赖 a。
调试建议:关注脏状态与 scheduler
computed 内部有 _dirty 标志和 scheduler 机制。当它依赖的响应式数据变化时,它会被标记为 dirty;下次读取时才会重新计算。你可以通过以下方式观察行为:
- 在每个 computed 的 getter 里加 console.log,看打印顺序
- 避免在 getter 中做副作用(如发请求、改 state),否则会干扰依赖追踪
- 用 vue-devtools 查看 computed 的依赖树和缓存状态


















