vue 3 中 computed 返回引用类型时,修改内部属性不触发重算且可能污染源数据;其响应性仅作用于整体值替换,内部属性需显式用 reactive/ref 或拆分为细粒度 computed 处理。

Vue 3 中 computed 返回引用类型(如对象、数组)时,修改其内部属性不会触发重新计算,但可能意外改变源数据,带来隐性 bug。
返回对象/数组时,计算属性本身仍是响应式的
计算属性的响应性只作用于“整个值的替换”,不深入追踪内部属性变化。例如:
const count = ref(1)
const obj = computed(() => ({ num: count.value }))
此时 obj.value 是一个普通对象,不是响应式代理。你写 obj.value.num = 99 不会更新视图,也不会触发 computed 重算,因为 count 没变,且 obj.value 的赋值没走 Vue 的响应式系统。
修改内部属性可能污染原始数据
如果计算属性返回的是对响应式对象的直接引用(比如 reactive 或 ref 的解包结果),改内部属性就等于改源数据:
const state = reactive({ a: 1 })const derived = computed(() => state)-
derived.value.a = 2→ 实际改了state.a,会触发依赖state的其他响应式逻辑
这不是计算属性“生效”了,而是你绕过了计算逻辑,直接动了源头。
需要响应式嵌套时,得主动创建响应式结构
若希望内部属性也能驱动更新,不能靠 computed 自动处理,而要显式使用 reactive 或 ref:
- 用
computed(() => reactive({ ... }))—— 不推荐,每次访问都新建响应式对象,失去缓存意义 - 更合理的是:把复杂结构拆成多个细粒度的
computed,或用watch+reactive手动同步 - 典型场景如表单联动:用
computed返回基础字段,再用reactive包裹整个表单状态
安全做法:默认当作只读快照来用
Vue 官方明确建议:计算属性是派生状态的“临时快照”。即使它返回对象,也应避免直接修改其属性:
- 想更新?改它的依赖项(比如
count.value++),让computed自动重算并返回新对象 - 想深响应?用
shallowRef+triggerRef或封装为自定义 Hook - 模板中绑定对象属性(如
{{ obj.name }})没问题,但脚本里别写obj.value.name = 'x'
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!











