Vue嵌套Ref修改为何丢失响应性?原因及正确处理方式
问题根源
子组件里的element是在组件初始化阶段直接赋值的:
const element = window.entity.value.propAr[props.id];
这个赋值只会执行一次,之后无论父组件怎么更新window.entity,子组件的element始终指向初始化时拿到的旧对象/数组元素引用,不会自动同步新数据。
逐个解析更新方法
有效方法working()
这个方法没有替换任何对象或数组的引用,只是直接修改了已有响应式对象的属性:
destObj.propAr[0].sc1 = srcObj.propAr[0].sc1;
Vue的响应式系统会追踪对象属性的变化,子组件的element还是原来的那个对象,属性值更新后,视图自然会同步刷新。
无效方法notWorking1()
直接替换了window.entity.value的整个引用:
window.entity.value = cloneDeep(entityB);
此时window.entity指向了一个全新的对象,但子组件的element仍然是初始化时从旧对象的propAr里拿到的旧元素,没有任何逻辑触发子组件重新获取新对象里的元素,所以视图停留在初始值。
无效方法notWorking2()
Object.assign把新对象的属性合并到旧的entity.value中,但新对象的propAr是一个全新的数组:
Object.assign(window.entity.value, cloneDeep(entityB));
子组件的element还是指向旧数组里的旧元素,新数组的元素并没有被关联到子组件,所以视图不更新。
正确处理方式
核心思路是让子组件的element能自动响应window.entity或props.id的变化,最简洁的方式是用computed计算属性:
修改子组件代码:
<script setup lang="ts"> const props = defineProps({ id: { type: Number, required: true }, }); // 用computed追踪响应式依赖,自动更新 const element = computed(() => window.entity.value.propAr[props.id]); </script> <template> <pre class="card">{{ element }}</pre> </template>
computed会自动监听window.entity.value和props.id的变化,一旦其中任何一个依赖更新,就会重新计算element的值,子组件视图也会同步刷新。
额外建议
实际项目中不要把ref挂载到window上(这只是示例替代方案),用Pinia的话,直接在子组件中引入store,通过computed读取store的state即可,Pinia会自动处理响应式追踪,写法更规范:
<script setup lang="ts"> import { useYourStore } from '@/stores/yourStore'; const props = defineProps({ id: Number }); const store = useYourStore(); const element = computed(() => store.entity.propAr[props.id]); </script>
内容的提问来源于stack exchange,提问作者Peter Hoehne

