父组件传参的差异,导致子组件监听参数时发生奇怪现象

先上代码

js 复制代码
父组件
<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中status的值,并不是一个简单的字符'0',而是关联了formConfig.value的内存地址。当需要获取status时,会通过内存地址找到formConfig.value,然后再拿到status。所以 当formConfig.value重新赋值时,内存地址变化,即使组件模板没有更新的状态下,form在深度监听状时,也会被触发变动。此时的form类似于 const form = computed(() => ({ status: formConfig.value.status }))

3.如果改变下父组件form的入参形式

js 复制代码
<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的更新,因为form中的status就是一个字符'0', 与status.value没有建立关联
  • 点击changeBtn2,因为msg.value变化了,触发模具更新,form会重新指向一个新的对象,子组件form的监听被触发
  • 点击changeBtn3,与点击changeBtn2一样的道理,即使没有status.value的任何变化,也会触发子组件form的监听

4.如果再改变下父组件form的入参形式

js 复制代码
<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的监听。

相关推荐
不法19 分钟前
uniapp map地图导航/地图路线
前端·vue.js·uni-app
卓怡学长3 小时前
w263基于springboot + vue 健康饮食管理系统
java·数据库·vue.js·spring boot·spring·intellij-idea
无情的西瓜皮4 小时前
MCP 协议实战:从零搭建一个 MCP 服务器(Node.js 版)
vue.js·人工智能·typescript·node.js
万亿少女的梦1684 小时前
基于Spring Boot、Vue.js和MySQL的超市管理系统设计与实现
java·vue.js·spring boot·mysql·管理系统
李明卫杭州5 小时前
Vue2 portal-target 组件与 Vue3 的针对性优化
前端·javascript·vue.js
江华森7 小时前
Vue Router 与 SPA 原理深度解析
前端·javascript·vue.js
糖墨夕7 小时前
第一章:为什么你要学 AI Agent?—— 从"切图仔"到全栈的进化之路
前端·javascript·vue.js
pany17 小时前
做 AI 友好的开源 Vue3 模板 🌈
前端·vue.js·ai编程
daols8819 小时前
vue 甘特图 vxe-gantt 小时视图设置日期轴每间隔 3 个小时
vue.js·甘特图·vxe-gantt
你挚爱的强哥1 天前
Vue2 实现 1.5s 十连击监听(连续点击若干次),封装通用可复用点击检测工具,不用 data!Vue 封装通用连击监听方法,支持自定义时长与点击次数
前端·javascript·vue.js