前言
Vue中的组件通讯是指在不同的Vue组件之间传递数据或触发行为的能力。由于Vue鼓励组件化开发,即将用户界面分解成可复用的小组件,因此组件之间的通信是构建复杂应用的关键部分。Vue提供了多种方式来实现组件间的通信,确保数据能够在组件树中正确流动。
简单的来说,通讯指的就是把我的数据给到你能用,我把我这里的东西和内容传输给你。
今天,咱们就来聊一聊vue中组件通讯的几种方式
正文
父子通讯
父子组件通讯,父组件将值v-bind绑定传给子组件,组件使用defineProps接受。
接下来我给大家举个例子聊聊,来看这段代码:
父组件
<template>
<div class="input-group">
<input type="text" v-model="value">
<button @click="add">添加</button>
</div>
<Child :msg="toChild"></Child>
</template>
<script setup>
import Child from "@/components/child.vue"
import { ref } from 'vue';
const value=ref("")
const toChild=ref("")
const add=()=>{
toChild=value.value
}
</script>
<style lang="css" scoped></style>
在父组件模板部分:
- 输入框绑定了
v-model="value",意味着它的值会与父组件中的value变量双向绑定。 - 按钮上的点击事件
@click="add",当点击时会调用父组件中的add方法。 - 子组件
<Child>被引入,并通过:msg="toChild"传递一个名为msg的属性,其值为toChild变量。
在脚本部分:
- 使用
ref函数创建了两个响应式引用:value用于存储输入框的值,初始为空字符串;toChild也初始化为空字符串,用于暂存要传递给子组件的数据。 - 定义了一个名为
add的方法,当按钮被点击时,会将value的值赋给toChild。这意味着输入框的内容会作为消息传递给子组件。
子组件
<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:" "
})
watch(
()=>props.msg,
(newVal,oldVal)=>{
list.value.push(newVal)
}
)
</script>
<style lang="css" scoped></style>
在子组件中咱们
- 导入了
ref和watch函数来自vue,用于创建响应式引用和监听属性变化。 - 声明了一个响应式引用
list,初始值是一个包含"html","css","js"字符串的数组。 - 使用
defineProps函数定义了组件接受的属性,这里只定义了一个名为msg的属性,类型默认为任何类型。 - 利用
watch函数监听props.msg的变化。每当msg的值发生变化时,回调函数会被触发,新值 (newVal) 会被推送到list数组中。这意味着每当父组件通过msg属性传递一个新的值,这个值就会被添加到子组件的列表中。
通过这样父组件将值v-bind绑定传给子组件,组件使用defineProps接受咱们就实现父子组件之间的通讯。
子父通讯
子父组件通讯通常有三种方式,下面我将分别通过上面的代码实例给大家介绍一下子父通讯之间的三种方式。
法一
子父组件通讯,子组件通过defineEmits(["add1"])创建一个自定义事件,并通过emits("add1",value.value)传递数据给父组件。父组件则可以在使用子组件的标签上通过v-on来监听这个事件。
父组件
<template>
<!-- 订阅add1事件 -->
<Child @add1="handle"></Child>
<div class="child">
<ul>
<li v-for="item in list">
{{ item }}
</li>
</ul>
</div>
</template>
<script setup>
import Child from "@/components/child2.vue";
import {ref} from "vue"
const list =ref(["html","css","js"])
const handle=(event)=>{
list.value.push(event)
}
</script>
<style lang="css" scoped></style>
在父组件中
1.引入了子组件 Child ,并通过 @add1="handle" 监听子组件触发的 add1 事件。
2.定义了一个响应式数组 list ,初始值包含 "html"、"css"、"js" 。
3.handle 函数用于处理接收到的 add1 事件,将事件传递的数据添加到 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 emits=defineEmits(["add1"]);//创建一个add事件
const add=()=>{
//将value给到父组件
emits("add1",value.value)//发布事件
}
</script>
<style lang="css" scoped>
</style>
在子组件中
const emits = defineEmits(["add1"]);使用defineEmits来声明组件向父组件发出的自定义事件。这里声明了一个名为add1的事件。const add = () => { emits("add1", value.value); }定义了一个方法add,当按钮被点击时调用。这个方法通过调用emits函数,触发名为add1的自定义事件,并将value的当前值作为参数传递给事件处理函数。
法二
子父组件通讯--- 父组件借助v-model将数据绑定给子组件,子组件创建"update:xxx"事件,并接收到的数据修改后emits出来。
父组件
<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 Child from "@/components/child3.vue";
import { ref } from "vue"
const list = ref(["html", "css", "js"])
</script>
<style lang="css" scoped></style>
在父组件的模板部分:
- 使用了
Child组件并通过v-model:list语法糖订阅子组件暴露出来的数据。这里list是自定义的模型修饰符名称,它实际上是利用Vue的v-model语法来简化数据的双向绑定。这意味着子组件中的某些数据将会被绑定到父组件的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 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)//抛出来修改后的数组,在父组件中有v-model: 会促使它去修改数据
}
</script>
<style lang="css" scoped>
</style>
在子组件中
-
使用
defineProps定义了组件接收的属性,这里接收一个名为list的数组属性,其默认值是一个空数组。 -
使用
defineEmits定义了组件可以发出的事件,这里定义了一个名为update:list的事件,用于通知父组件更新list数组。 -
定义了一个add方法,当按钮被点击时执行:
首先,咱们不直接修改
props.list(避免突变原始数据带来的问题),先创建了props.list的一个副本arr。然后,将
value的值推入到副本数组arr中。最后,通过
emits("update:list", arr)触发自定义事件,抛出来修改后的数组,在父组件中有v-model: 会促使它去修改数据,并将修改后的数组作为参数传递给父组件,父组件通过监听这个事件可以更新其状态。
法三
子父组件通讯,父组件通过ref获取子组件中defineExpose()暴露出来的数据。
父组件
<template>
<Child ref="childRef"></Child>
<div class="child">
<ul>
<!-- 加个问号,childRef有值就会去读取list -->
<li v-for="item in childRef?.list">
{{ item }}
</li>
</ul>
</div>
</template>
<script setup>
import Child from "@/components/child4.vue";
import { onMounted, ref } from "vue"
const childRef=ref(null)
</script>
<style lang="css" scoped></style>
在父组件中的模板部分
- 引入了子组件
Child并为其分配了一个模板引用ref="childRef"。这意味着父组件可以通过childRef访问到子组件的实例。 - 在一个
ul列表中,使用v-for指令遍历childRef?.list。这里使用了可选链操作符?.,确保只有当childRef不为null或undefined时才会访问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:list})//心甘情愿暴露出来list
</script>
<style lang="css" scoped>
</style>
这里在子组件中直接将list数组放在子组件里,父组件中不要了,这样一来的好处是什么呢?就是子组件要把input框中的值往数组往父组件中放就会变的很简单,直接子组件自己拿自己的value放到自己的list中去就行了,就不需要传递啦,这时候父组件就只需要享用现成的就行啦,这里咱们通过在父组件中引入子组件 Child ,并通过 ref="childRef" 为子组件设置引用.同时设置一个变量childRef,默认为null,将它作为一个标记打到上面的Child组件身上。那么这样一来我们就可以通过这个childRef访问到整个子组件。包括子组件中的内容和变量,当然这里咱们子组件中还需要把list暴露出来让父组件取.
-
defineExpose({ list: list })使用此函数暴露组件内部的list变量,使得使用该组件的外部组件能够访问到这个数据。也就是心甘情愿暴露出来list.这是一种在Composition API中明确指定哪些属性或方法对外暴露的方式,便于组件间的数据共享和通信。
总结
本文给大家介绍vue的组件通讯方法,一种父子组件通讯,三种子父组件通讯,为什么只有一种父子组件通讯呢,这一种已经极致的优雅了,父直接传,子直接接受就行啦。很舒服。好啦,今天的分享就到这里啦,可以点个免费的赞赞嘛,感谢感谢!