一、先看例子:
<script setup lang="ts">
import { onMounted, reactive, ref, watch } from 'vue';
import Test from '@/components/Test.vue';
let a = {a:"a"};
const aRef = ref(a);
var aReactive = reactive(a);
let bObj = "B";
const bObjRef = ref(bObj);
watch(testB, async (newAttr, oldAttr) => {
let cReactive = reactive({a:newAttr});
aReactive = cReactive;
})
</script>
<template>
<div style="display: block;">
<input v-model="testB"/>
<Test :a="a" :aRef="aRef" :aReactive="aReactive" :bObj="bObj" :bObjRef="bObjRef" ></Test>
</div>
</template>
<script setup lang="ts">
import { computed, reactive, ref, watch } from 'vue';
const props = defineProps({
a:Object,
aRef:{type:Object, required:true},
aReactive:Object,
bObj:String,
bObjRef:String,
});
const aReactive = props.aReactive;
</script>
<template>
<div> props.a -> {{props.a}} </div>
<div> props.aRef -> {{props.aRef}} </div>
<div> props.aReactive -> {{props.aReactive}} </div>
<div> aReactive -> {{aReactive}} </div>
<div> props.bObj -> {{props.bObj}} </div>
<div> props.bObjRef -> {{props.bObjRef}} </div>
<van-cell-group inset>
<van-field v-model="aReactive.a" label="标题" placeholder="输入框左侧文本" >
</van-field>
</van-cell-group>
</template>
<style>
</style>
我们在我们在父组件修改输入框testB的值,会发现,子组件响应式更新的只有以props开始访问的模块会自动更新,其他以aReactive开始访问的没有更新。
二、为什么会这样呢?
首先我们可以确定aReactive是一个reactive对象。为什么其响应式丢失了呢?
这里面究竟发生了什么?
实际逻辑分析如下:
- 用户在父组件的输入框输入新的字符串。
- 触发监听,然后脚本修改aReactive的值。
- 由于子组件的props具有响应性,其内在触发器触发,props.aReactive的修改为了cReactive
- 子组件中const aReactive = props.aReactive; 此时不会再次执行,该脚本只在子组件初始化时执行了一次,这行代码并没有在props响应式的触发器范围内。所以子组件内的变量aReactive值reactive类型,其代理的值还是之前{a:"a"},而props.aReactive的值也是reactive类型,但其代理的值是{a:"C"}
- 所以最终结果就出现了上面的情况。
原因分析清楚了,那么接下来如何达到我们想要的结果呢?
三、解决办法
最简单直接的办法就是使用computed。如:
const aReactive = computed(()=>{return props.aReactive});;
当然也可以使用其他方法,类似watch等。
const aReactive = ref(props.aReactive);
watch(()=>props.aReactive, (newAttr, oldAttr) => {
aReactive.value = newAttr;
})
四、总结
造成响应式丢失的原因就是没有正确的获取到代理对象。aReactive对象获取不对,所以导致响应性丢失。