1.引言
在Vue中,组件通信是非常重要的一个主题,它涉及到不同组件之间的数据交换和消息传递,而Vue提供了多种方式来实现组件间的通讯。本文将从父子组件通信和子父组件通信两个方面进行介绍。
1.1 引入案例介绍
这个案例非常简单,总共分为2部分,第一部分由一个input框和一个button按钮组成,第二部分通过ul、li进行数据渲染。
<template>
<!-- 第一部分 -->
<div class="input-group">
<input type="text" v-model="value">
<button @click="add">添加</button>
</div>
<!-- 第二部分 -->
<div class="child">
<ul>
<li v-for="item in list">{{ item }}</li>
</ul>
</div>
</template>
<script setup>
import { ref } from 'vue';
const list = ref(['red', 'green', 'black']);
const value = ref('');
const add = () => {
if (value.value !== '') {
list.value.push(value.value);
value.value = '';
}
}
</script>
<style lang="scss" scoped></style>
下面我们将分别将第一部分和第二部分作为子组件来讲解vue中组件通信的方式。
2.父子组件通信(父组件传输数据给子组件)
创建一个新的vue文件(child.vue),将原vue文件(App.vue)的第二部分移至新文件中,再将新vue import到原vue文件中。
这样App.vue就是父组件,child.vue则是子组件。
2.1 情景一:子组件只进行数据渲染
2.1.1 父组件发送数据
父组件通过v-bind绑定将数据发送给子组件
<Child>
:父组件中子组件的声明:list="list"
::list
是一种简写形式,等同于v-bind:list
,用于将父组件的数据list
绑定到子组件的list
属性。list
:传递给子组件的数据名称,即父组件中定义的变量名。父组件中的list
变量通过这种方法传递给了子组件。
子组件在接收到这个数据后,根据需要对其进行渲染。
2.1.2 子组件接收数据
子组件通过defineProps来接收数据。
- 使用
defineProps
定义接收list
作为属性。 - 设置
list
的类型为数组,并提供默认值为空数组。 - 如果传输数据不是Array,则
报错
。
2.2 情景二:子组件进行数据渲染 + 数据增加
与情景一不同的是父组件只需要将input添加的字符串传输给子组件,而不需要传输整个list数组。
2.2.1 父组件发送数据
创建一个新的变量toChild来存储input框里输入的值,并通过msg
prop 来传递这个数据。
2.2.2 子组件接收数据
-
此情景下的defineProps是相对于情景一的
简写
,不会对传输的数据类型进行校验。 -
watch(() => props.msg, (newVal, oldVal) => { ... })
:() => props.msg
:使用箭头函数返回props.msg
的值,即监视props.msg
的变化。(newVal, oldVal) => { ... }
:回调函数,当props.msg
变化时被触发。newVal
表示新的值,oldVal
表示之前的值。
-
(newVal, oldVal) => { list.value.push(newVal); }
:- 在回调函数中,当
props.msg
发生变化时(新值为newVal
),将新值添加到list
数组中。
- 在回调函数中,当
当父组件传递给子组件的 msg
数据发生变化时,通过 watch
监听该变化,并将新值添加到子组件内部的 list
数组中。这样就实现了响应式地更新子组件中展示的列表数据,保持数据同步和反应性。
如果不使用watch,父组件传输的值永远为 '' 空字符串,在点击添加button后也不会再次再次响应。
3.子父组件通信
创建一个新的vue文件(child.vue),将原vue文件(App.vue)的第一部分移至新文件中,再将新vue import到原vue文件中。
这样App.vue就是父组件,child.vue则是子组件。
3.1 方式一:发布订阅机制
借助发布订阅机制,子组件负责发布事件并携带参数,父组件订阅该事件通过事件参数获取子组件提供的值。
3.1.1 子组件发送数据——仅发送input的value
子组件需要将input的value传递给父组件。
-
const emits = defineEmits(['add1']);
:defineEmits
是一个 Vue 3 Composition API 提供的函数,用于定义子组件可以发出的自定义事件。- 在这里,通过
defineEmits(['add1'])
定义了一个名为add1
的自定义事件。
-
emits('add1', value.value);
:调用emits
函数,通过名称'add1'
发出一个名为add1
的自定义事件,并传递value.value
作为事件的数据。
3.1.2 父组件接收数据
<Child @add1="handle"></Child>
: 监听add1
事件,事件触发时执行handle
方法。event
为子组件发布事件时传递的事件数据
。
3.2 方式二:v-model
父组件借助v-model将数据绑定给子组件,子组件创建'updata:xxx'事件,并将接收到的数据修改后emits出来
3.2.1 子组件发送数据——发送整个list
- 通过
defineEmits
定义了一个名为emits
的对象,其中包含了一个名为update:list
的事件。 const arr = props.list;
:创建一个指向父组件传递的list
数据的引用。arr.push(value.value);
:将当前组件内部的value
值添加到arr
数组中。emits('update:list', arr);
:通过emits
触发update:list
的事件,并传递修改后的arr
数组,新的数据传递给父组件。
为什么不使用更为简单的 props.list.push(value.value)?
- 单向数据流
- Vue 中的 props 是单向数据流的,即父组件向子组件传递数据,子组件应当尽量避免直接修改父组件传递的 props 数据。这是为了维持数据流的清晰性和可预测性。
- 避免数据混乱和副作用
- 当直接在子组件中修改 props 时,可能导致意外的数据变化,特别是在大型应用中很难追踪数据变化的来源。这样的操作会增加代码的复杂性和出错的可能性。
3.2.2 父组件接收数据
v-model:list="list"
:将list
作为v-model
绑定到Child
子组件,这意味着Child
组件可以修改和更新list
的值。
3.3 方式三:ref
父组件通过ref获取子组件中的defineExpose() 暴露出来的数据。
3.3.1 子组件发送数据——发送整个list
- 使用
defineExpose
暴露list
属性,使父组件能够通过childRef
访问到子组件中的list
。
3.3.2 父组件接收数据
Child
组件被渲染并通过ref
属性绑定到childRef
引用。- 使用
ref
创建一个childRef
,初始值为null
,用于引用子组件实例。
结语
通过上述介绍,我们可以看到在Vue中实现组件通讯有多种方式,开发者可以根据具体的场景和需求选择适合的方式来进行组件通讯,从而实现更灵活和高效的前端开发。希望本文能对大家有所帮助,感谢阅读!
原文:https://juejin.cn/post/7384327143785381939