前言
今天讲讲Vue的组件通信的几种方式
正文
父子通讯 – 父给子
父组件将值 v-bind 绑定传给子组件,子组件通过 degineProps 接收
接下来用一个代码示例讲解一下
-  父组件 - value是使用- ref创建的响应式变量,用于绑定输入框的值。
- to也是使用- ref创建的响应式变量,用于向子组件传递数据。
- add方法在点击“添加”按钮时被触发。如果- value的值不为空,就将其赋值给- to。
- 在模板中,有一个输入框 v-model绑定了value,一个“添加”按钮绑定了add方法,还使用<Child :msg="to" />将to的值传递给子组件Child。
 
<template><div class="input-group"><input type="text" v-model="value" /><button @click="add">添加</button></div><Child :msg="to"></Child>
</template><script setup>
import { ref } from "vue";
import Child from "@/components/child.vue";const value = ref("");
const to = ref('')
const add = () => {if (value.value !== "") {to.value = value.value ;}
};
</script><style lang="css" scoped></style>
-  子组件: - list是使用- ref创建的响应式数组,初始值包含- "html"、- "css"、- "js"。
- 使用 defineProps接收来自父组件的msg属性。
- 直接将 props.msg添加到list数组中。
- 使用 watch监听props.msg的变化。当props.msg发生变化时,将新值添加到list数组中,并在控制台打印新值和旧值。
 
<template><div class="child"><ul><li v-for="item in list">{{ item }}</li></ul></div>
</template><script setup>
import { ref, watch } from "vue";
const list = ref(["html", "css", "js"]);const props = defineProps({msg: "",
});
list.value.push(props.msg);watch(() => props.msg,(newVal, oidVal) => {console.log(newVal, oidVal);list.value.push(newVal);}
);
</script><style lang="css" scoped></style>
子父通讯 – 子给父
第一种方式(发布订阅)
借助发布订阅机制,子组件通过发布并携带参数,父组件订阅事件通过事件提供的函数获取值
- 父组件: - 引入了子组件 Child,并通过@add1="handle"监听子组件触发的add1事件。
- 定义了一个响应式数组 list,初始值包含"html"、"css"、"js"。
- handle函数用于处理接收到的- add1事件,将事件传递的数据添加到- list数组中。
 
- 引入了子组件 
<template><!-- 订阅add1事件  --><Child @add1="handle"></Child><div class="child"><ul><li v-for="item in list">{{ item }}</li></ul></div>
</template><script setup>
import { ref } from "vue";
import Child from "./components/child2.vue";const list = ref(["html", "css", "js"]);const handle = (event) => {list.value.push(event);
};
</script><style lang="css" scoped></style>- 子组件 - 定义了一个输入框 v-model绑定了value,以及一个“添加”按钮。
- 使用 defineEmits创建了一个名为add1的自定义事件。
- add方法在点击“添加”按钮时被触发,通过- emit("add1", value.value)向父组件触发- add1事件,并将- value的值作为参数传递给父组件。
 
- 定义了一个输入框 
<template><div class="input-group"><input type="text" v-model="value" /><button @click="add">添加</button></div>
</template><script setup>
import { ref } from "vue";const value = ref("");const emit = defineEmits(["add1"]); // 创建一个add事件
const add = () => {// 将数据传递给父组件emit("add1", value.value); // 第一个参数是事件名,第二个参数是要传递的数据 发布事件
};
</script><style lang="css" scoped></style>第二种方式(v-model)
子父组件通讯 父组件借助 v-model 将数据绑定给子组件,子组件创建"update:XXX"事件,并接收到数据修改后 emits 出来
-  父组件 - 在模板中使用 <Child v-model:list="list" />,通过v-model语法与子组件进行数据交互,将父组件中的list数据传递给子组件,并能接收子组件对list的修改。
- 利用 ref创建了名为list的响应式数组,其初始值为["html", "css", "js"],并在模板中通过v-for遍历展示其元素。
 
- 在模板中使用 
<template><Child v-model:list="list"></Child><div class="child"><ul><li v-for="item in list">{{ item }}</li></ul></div>
</template><script setup>
import { ref } from "vue";
import Child from "./components/child3.vue";const list = ref(["html", "css", "js"]);
</script><style lang="css" scoped></style>- 子组件 - 模板中有一个输入框 v-model绑定了value和一个点击触发add方法的“添加”按钮。
- 通过 defineProps定义了从父组件接收的list属性,指定其类型为数组,并提供了默认的空数组。
- 利用 defineEmits定义了名为update:list的自定义事件,用于向父组件传递数据更新。
- 在 add方法中,为了避免直接修改父组件传来的props.list,先复制了一份到arr,然后向arr中添加新值,最后通过触发Emits("update:list", arr)事件将更新后的数组传递回父组件,实现与父组件的双向数据绑定。这种复制后修改再传递的方式,是为了遵循 Vue 的单向数据流原则,保证数据的准确性和可预测性。
 
- 模板中有一个输入框 
<template><div class="input-group"><input type="text" v-model="value" /><button @click="add">添加</button></div>
</template><script setup>
import { ref } from "vue";const value = ref("");const props = defineProps({list: {type: Array,default: () => [],},
});const emits = defineEmits(["update:list"]);
const add = () => {//   props.list.push(value.value);// 不建议直接操作父组件给过来的数据const arr = props.list;arr.push(value.value);emits("update:list", arr);
};
</script><style lang="css" scoped></style>-  父组件 - 引入子组件 Child,并通过ref="childRef"为子组件设置引用。
- 在模板中,尝试通过 childRef?.list访问子组件暴露出来的list属性,并使用v-for进行遍历展示。
- 使用 ref创建了childRef,初始值为null。
- 在 onMounted钩子函数中,打印childRef.value,即在组件挂载后获取子组件的引用。
 
- 引入子组件 
<template><Child ref="childRef"></Child><div class="child"><ul><li v-for="item in childRef?.list">{{ item }}</li></ul></div>
</template><script setup>
import { ref, onMounted } from "vue";
import Child from "./components/child4.vue";const childRef = ref(null);onMounted(() => {console.log(childRef.value);
});
</script><style lang="css" scoped></style>- 子组件 - 包含一个输入框 v-model绑定了value和一个“添加”按钮。
- 定义了 value和list两个响应式变量。
- add方法用于向- list中添加新值。
- 通过 defineExpose({ list })主动暴露list属性,使得父组件能够通过引用获取到。
 
- 包含一个输入框 
<template><div class="input-group"><input type="text" v-model="value" /><button @click="add">添加</button></div>
</template><script setup>
import { ref } from "vue";const value = ref("");
const list = ref(["html", "css", "js"]);const add = () => {list.value.push(value.value);
};defineExpose({ list }); // 心甘情愿暴露出来
</script><style lang="css" scoped></style>这里使用childRef?的原因:
ref 用于注册引用信息,可以通过 ref 获取 DOM 元素或组件实例。在父组件中,使用 ref 为子组件创建一个引用,在模板中可以通过 ref 访问子组件的属性和方法。
然而,在访问子组件的属性或方法时,可能会存在子组件尚未完全渲染的情况。由于 Vue 的异步渲染策略,子组件的 DOM 元素可能需要一些时间才能完全渲染。如果在父组件的钩子函数中直接访问子组件的属性或方法,可能会导致获取到不完整或错误的信息。
总结
本文讲解了组件通信中的父子组件通信和子父组件通信,希望看到这里的你能够有所收获!!!