计算属性死循环的根本原因是getter中修改了自身依赖的数据,导致“读→写→再读”无限循环;它只应派生值,不可执行副作用,修改操作须移至methods或watch中。

计算属性引发死循环,根本原因不是 Vue 有缺陷,而是你在它的 getter 函数里偷偷改了它自己依赖的数据——读的时候顺手写了,写完又触发重新读,形成“读→写→再读→再写”的无限链路。
核心问题:computed 只该“算”,不该“动”
计算属性的设计定位是派生状态:输入变,它跟着安全地重新算出一个值。它不负责修改数据、不发请求、不调 API、不 push 数组、不赋值响应式字段。
- ❌ 错误写法:在 getter 里直接给
this.count++、this.list.push(item)、this.user.name = 'new' - ✅ 正确做法:把这类动作移出去——用
methods处理用户操作,用watch响应变化后执行副作用
容易被忽略的隐式写入场景
你以为只是“读”,其实已悄悄触发 setter:
- 对 reactive 对象做解构再赋值回原对象(如
const { name } = this.user; this.user.name = name + '!') - 用
JSON.parse(JSON.stringify(obj))深拷贝后再赋值,导致响应式连接断裂+重赋值 - 访问嵌套属性时用了可写代理(比如
this.form?.profile?.age ?? 0安全,但this.form.profile.age = 25就危险)
快速定位循环源头的实操方法
别靠猜,用工具直击关键点:
- 在 computed 的 getter 第一行加
console.trace('userFullName computed'),看控制台是否反复打印同一行 - 打开 Vue Devtools → “Reactivity” 面板 → 找到该计算属性 → 查看依赖图,如果它自己出现在自己的依赖列表里,就是典型的反向依赖闭环
- 检查是否有其他 computed 或 watch 无意中修改了同一个响应式源,间接造成连锁触发
替代方案:什么情况下该用 watch 而不是 computed
当你需要的是“某值变了,就做点事”,而不是“我要一个随它变的只读值”:
- 需要调接口、更新 localStorage、触发动画、改变另一个 ref 状态 → 用
watch - 需要缓存中间结果?配合一个独立的
ref存储,watch 负责更新它 - 想保持模板简洁?可以把 watch 逻辑封装成函数,在 setup 中调用,而非塞进 computed









