先上代码
父组件
<template>
<div>
<Child :msg="msg" :form="{ status: formConfig.status }" />
<button @click="changeBtn">change</button>
</div>
</template>
<script setup lang="ts">
import { ref } from 'vue';
import Child from './child.vue';
const msg = ref('keke');
const formConfig = ref({
status: '0',
});
const changeBtn = () => {
msg.value = 'keke2';
formConfig.value.status = '0';
};
</script>
子组件
<template>
<div>
<h1>{{ msg }}</h1>
</div>
</template>
<script setup lang="ts">
import { toRefs, watch } from 'vue';
const props = defineProps<{
msg: string;
form: {
status: string;
};
}>();
const { form } = toRefs(props);
watch(
form,
(newVal: unknown, oldVal: unknown) => {
console.log('form:', newVal, oldVal);
},
{ deep: true, immediate: true },
);
</script>
奇怪现象:当父组件中点击按钮后,子组件中竟然触发了form的监听,但是form本身的值并没有任何的改变,而且它的内存地址引用也没有发生改变。
解释:父组件我们点击按钮时,msg.value的值发生了变更,触发了模板的更新,而form的传参是在模板中展开的,这导致模板在更新时,针对form会重新创建一个新对象,虽然值和之前的相同,但是内存地址已经发生了变化。
1.如果,在changeBtn时不改变msg的值, const changeBtn = () => { formConfig.value.status = '0';},点击按钮后,就不会触发子组件中form的监听
2.如果 const changeBtn = () => { formConfig.value = { status: '0' } }; 这样就可以重新触发子组件form的监听。此时的form等效于 const form = computed(() => ({ status: formConfig.value.status })),formConfig.value内存地址的重新变化,自然会通知到引用它的computed重新更新。
3.如果改变下父组件form的入参形式
<template>
<div>
<Child :msg="msg" :form="{status: status}" />
<button @click="changeBtn">change</button>
<button @click="changeBtn2">change</button>
<button @click="changeBtn3">change</button>
</div>
</template>
<script setup lang="ts">
import { ref } from 'vue';
import Child from './child.vue';
const msg = ref('keke');
const status = ref('0')
const changeBtn = () => {
status.value = '0';
};
const changeBtn2 = () => {
msg.value = 'keke2'
status.value = '0';
};
const changeBtn2 = () => {
msg.value = 'keke2'
};
</script>
- 点击changeBtn,不会触发子组件form的更新
- 点击changeBtn2,因为msg.value变化了,触发模具更新,form会重新指向一个新的对象,子组件form的监听被触发
- 点击changeBtn3,与点击changeBtn2一样的道理,即使没有status.value的任何变化,也会触发子组件form的监听
4.如果再改变下父组件form的入参形式
<template>
<div>
<Child :msg="msg" :form="formConfig" />
<button @click="changeBtn">change</button>
</div>
</template>
<script setup lang="ts">
import { ref } from 'vue';
import Child from './child.vue';
const msg = ref('keke');
const formConfig = ref({
status: '0',
});
const changeBtn = () => {
msg.value = 'keke2'
formConfig.value.status = '0';
};
</script>
此时,form指向的是formConfig.value的内存地址。当点击按钮时,msg的值发生变化,虽然模板更新了,但是form的指向并没有变,仍然指向formConfig.value的内存地址,而formConfig.value的内存地址也没有变化,所以,在子组件中,无法触发对form的监听。