在 Vue 3 的 <script setup>
语法中,如果使用 reactive
包裹的对象被重新赋值为一个新的对象,那么这个新对象将不再是响应式的。这是因为 reactive
函数只会在其被调用时使其参数对象成为响应式的,而后续对该变量的重新赋值(特别是赋值为一个新对象)并不会自动使新对象也变成响应式的。
原因
Vue 3 的响应式系统是基于 ES6 的 Proxy 实现的。当使用 reactive
包裹一个对象时,Vue 会创建一个该对象的 Proxy 实例,并返回这个 Proxy 实例。当修改这个 Proxy 实例的属性时,Vue 能够捕获这些修改并触发视图更新。但是,如果将 reactive
返回的 Proxy 实例重新赋值为一个全新的对象,那么这个新对象就没有被 Vue 的响应式系统处理过,因此它不会触发视图更新。
解决方式
-
避免重新赋值:
最直接的方式是避免对reactive
返回的对象进行整体重新赋值。如果你需要更新对象的内容,应该直接修改对象的属性。import { reactive } from 'vue'; const state = reactive({ value: 0 }); // 正确的方式:修改对象的属性 state.value = 1; // 错误的方式:重新赋值,这将导致失去响应性 // state = reactive({ value: 1 });
-
使用 Vue 3 的 Composition API 中的其他函数:
如果确实需要替换整个对象,并且希望新对象也是响应式的,可以考虑使用ref
(对于基本类型或对象引用)或再次调用reactive
(但通常不推荐这样做,因为它会绕过 Vue 的优化)。然而,对于对象来说,ref
并不直接适用,因为它会将对象作为单个响应式引用处理,而不是对象的每个属性。对于复杂情况,可能需要手动更新对象的属性,或者使用 Vue 3 提供的
toRefs
或shallowReactive
等函数来辅助处理。 -
使用
shallowReactive
:
如果你的对象结构很深,但你只需要浅层响应式(即只监听对象第一层属性的变化),可以使用shallowReactive
。然而,这并不会解决重新赋值后失去响应性的问题,只是改变了响应式跟踪的深度。 -
使用
Object.assign()
-
在 Vue 3 中,使用
Object.assign(state, { value: 1 })
来更新一个由reactive
包裹的对象state
,这种方法可以更新state
对象的value
属性,并且保持这个属性的响应性。但是,需要注意的是,这种方式并没有重新赋值给state
本身,而是修改了state
对象的属性。import { reactive } from 'vue'; const state = reactive({ value: 0 }); // 使用 Object.assign 更新 state 的 value 属性 Object.assign(state, { value: 1 }); //获取后端接口数据使用 // 或者直接赋值,效果相同 // state.value = 1; // 此时,如果视图中有依赖于 state.value 的部分,它将会更新
-