本文主要是讲述Vue3在setup语法糖下的组件间通信
Vue组件通信是指在Vue.js中,不同组件之间进行信息交流和共享数据的过程。在前端开发中,组件通信是非常重要的一部分,因为在一个复杂的应用中,不同的组件通常需要相互协作,共同完成一些功能。
一、父传子组件通信
父组件通过props将数据传递给子组件,子组件可以通过调用父组件传递的函数来与父组件进行通信。
下面通过几个实例来讲解:
实验一: 在父组件里,设置子组件的"字符串型"属性(进而用于父组件向子组件传递数据)
在父组件里,设置子组件的"字符串型"属性(propName="王五" propAge="21"),这就是父组件向子组件传递的数据
html
//父组件
<script setup>
import Header from "./components/Header.vue";
</script>
<template>
<!-- 实验一: 在父组件里,设置子组件的"字符串型"属性(进而用于父组件向子组件传递数据) -->
<Header propName="王五" propAge="21" />
</template>
<style scoped>
</style>
html
//子组件
<script setup>
// props是properties的缩写,意思为属性,
// defineProps定义的属性是提供给外部进行设置使用的
// 定义字符串类型的属性
const props = defineProps(
["propName", "propAge"]
)
console.log(props);
</script>
<template>
<div>
<h3>我是页面头部(子组件)</h3>
</div>
</template>
<style scoped>
/* 设置子组件样式 */
div {
width:25%;
background-color: red;
}
</style>
以上例子中,子组件通过 defineProps声明接收props
运行结果:
实验二: 在父组件里,设置子组件的"对象型"属性(进而用于父组件向子组件传递数据)
在父组件里,设置子组件的"对象型"属性
const propStudent = {
name: "李雷",
age: 19,
进而用于父组件向子组件传递数据;父组件在调用子组件时,使用 v-bind 绑定参数 propStudent ,并传给子组件
html
//父组件
<script setup>
import { reactive } from "vue";
import Nav from "./components/Nav.vue";
const propStudent = {
name: "李雷",
age: 19,
}
</script>
<template>
<!-- 实验二: 在父组件里,设置子组件的"对象型"属性(进而用于父组件向子组件传递数据)-->
<Nav v-bind="propStudent" />
</template>
<style scoped>
</style>
html
//子组件
<script setup>
// 定义对象类型的属性
const props = defineProps(
{
name:{
type:String // 类型为字符串
},
age:{
type:Number, // 类型为数字
// 是否必需传值(若必须传值却没有传,则控制台会给出警告)
required:true,
default:18 // 默认值
},
}
)
console.log(props);
</script>
<template>
<div>
<h3>我是页面导航(子组件)</h3>
</div>
</template>
<style scoped>
div {
width:25%;
background-color: greenyellow;
}
</style>
以上例子中,子组件使用 defineProps 声明接收props,并且定义对象类型的属性props ,而后就可以正常使用该参数
运行结果:
实验三: 在父组件里,设置子组件的"响应型"属性(进而用于父组件向子组件传递数据)
在父组件里,设置子组件的"响应型"属性( reactive ), 进而用于父组件向子组件传递数据
html
//父组件
<script setup>
import { reactive } from "vue";
import Footer from "./components/Footer.vue";
const propTeacher = reactive( {
name: "韩梅梅",
age: 25,
})
const addTeacherAge = () => {
propTeacher.age ++
console.log(`教师年龄为${propTeacher.age}`);
}
</script>
<template>
<!-- 实验三: 在父组件里,设置子组件的"响应型"属性(进而用于父组件向子组件传递数据) -->
<Footer v-bind="propTeacher" />
<button v-on:click="addTeacherAge">在父组件中增加教师年龄</button>
</template>
<style scoped>
</style>
上述父组件中,定义了 一个响应式数据 propTeacher 和一个方法 addTeacherAge 。父组件在调用子组件时,使用 v-bind 绑定参数 propTeacher ,并传给子组件;使用方法来增加教师的年龄
html
//子组件
<script setup>
// 定义对象类型的属性
const props = defineProps(
{
name:{
type:String // 类型为字符串
},
age:{
type:Number, // 类型为数字
required:true, // 是否必需传值(若必须传值却没有传,则控制台会给出警告)
default:28 // 默认值
},
}
)
</script>
<template>
<div>
<h3>我是页面底部(子组件), 教师年龄为: {{ props.age }}</h3>
</div>
</template>
<style scoped>
div {
width:25%;
background-color: rgb(154, 154, 228);
}
</style>
子组件中,使用 defineProps 声明接收props,并且定义对象类型的属性 props ,而后就可以正常使用该参数
二、子传父组件通信
子组件可以通过调用父组件传递的函数或者触发父组件的事件来与父组件进行通信
子组件传值给父组件主要是子组件通过 defineEmits 注册一个自定义事件,而后触发 emits 去调用该自定义事件,并传递参数给父组件。
html
//子组件
<script setup>
/* defineEmits是Vue3的编译时宏函数,
用于子组件向父组件发送自定义事件 */
//1.用宏函数defineEmits定义一个名为 emits 的对象(名字可以随便取), 用于存储自定义事件
const emits = defineEmits(["getPerson", "addPerson"])
//2.向上级组件发射名为"getPerson"的自定义事件,并同时发射数据
emits("getPerson", { name:"李雷", age: 18 })
const addPerson = () => {
//3.向父组件发射名为"addPerson"的自定义事件,并同时发射数据
emits("addPerson", 1)
}
</script>
<template>
<div>
<h3>我是下级组件</h3>
<button @click="addPerson">添加人数</button>
</div>
</template>
<style scoped>
div {
width: 25%;
background-color: gray;
}
</style>
上述子组件中, 通过 defineEmits 注册了一个名为 emits 的对象,包含两个自定义事件 getPerson 和 addPerson ,通过两种方法向上级组件发射名为"getPerson"和"addPerson"的自定义事件,并同时发射数据
在父组件中调用子组件时,通过 v-on 绑定一个函数,通过该函数获取传过来的值。
html
//父组件
<script setup>
import { ref } from 'vue';
import Header from './components/Header.vue';
const person_num = ref(1)
const emitPrintPerson = (data) => {
console.log(`下级组件发射过来的数据为: ${data.name} , ${data.age}`);
}
const emitAddPersonNum = (data) => {
person_num.value += data
}
</script>
<template>
<div id="root_component">
<h3>我是上级组件</h3>
<h6>计算机学院总人数:{{ person_num }}</h6>
<!-- 监听两个子组件的事件 -->
<Header v-on:getPerson="emitPrintPerson" @addPerson="emitAddPersonNum" />
</div>
</template>
<style scoped>
#root_component {
width: 50%;
background-color: antiquewhite;
}
</style>
上述父组件中,定义两个函数 emitPrintPerson 和 emitAddPersonNum ,调用子组件时通过 v-on 绑定这两个函数,通过该函数获取传过来的值 。
(在父组件上使用 v-on 设置响应函数
getPerson 和 addPerson ,
该函数与子组件中的注册自定义事件getPerson、addPerson
名称需一致)
运行结果:
三、跨组件通信 (依赖注入 provide 和 inject)
在React中,当需要在不直接存在父子关系的组件之间进行通信时,可以使用 provide 和 inject 来实现跨组件通信。一个父组件相对于其所有的后代组件,会作为依赖提供者。任何后代的组件树,无论层级有多深,都可以注入由父组件提供给整条链路的依赖。
例如就是这样一个组件树,APP组件跨过Header组件与Nav组件通信; 不过provide是向所有下层组件广播普通数据,父组件APP也可以传给子组件Header,用法是与传给孙组件一样的,这里就不一一赘述了。
provide 用于某个上层组件将数据广播给所有下层组件。若使用了 provide 和 inject 来进行数据传递,则一般不需要再使用 defineProps
下面通过几个实验来理解:
【实验1】某个上层组件向所有下层组件广播普通数据,下层组件通过inject注入上层App组件广播的普通数据
在父组件定义一个属性 web ,当父组件传值给所有子组件时,使用provide 将其进行注入
html
//父组件
<script setup>
import { provide, ref } from 'vue'
import Header from "./components/Header.vue"
//【实验1】某个上层组件向所有下层组件广播普通数据
const web = {name:"百度一下", url:"www.baidu.com"}
provide("provideWeb", web) //注入名,值
</script>
<template>
<h3>我是上层的App组件</h3>
<Header/>
</template>
<style scoped></style>
孙组件想要获取 provideWeb 的值,只需使用inject 即可
html
//孙组件
<script setup>
import { inject } from 'vue'
//【实验1】下层组件通过inject注入上层App组件广播的普通数据
const web = inject("provideWeb") //注入名,默认值
console.log("provideWeb:", web)
</script>
<template>
<h3>我是底层的Nav组件</h3>
</template>
<style scoped></style>
运行结果:
【实验2】某个上层组件向所有下层组件广播响应式数据,下层组件通过inject注入上层App组件广播的函数
在父组件定义一个响应式数据 user ,当父组件传值给所有子组件时,使用provide 将其进行注入
html
//父组件
<script setup>
import { provide, ref } from 'vue'
import Header from "./components/Header.vue"
//【实验2】某个上层组件向所有下层组件广播响应式数据
const user = ref(0)
provide("provideUser",user) //注入名,值
</script>
<template>
<h3>我是上层的App组件, 用户数为: {{ user }}</h3>
<Header/>
</template>
<style scoped></style>
孙组件想要获取 provideUser 的值,只需使用inject 即可 ,使用 .value 来获取其值
html
//孙组件
<script setup>
import { inject } from 'vue'
//【实验2】下层组件通过inject注入上层App组件广播的响应式数据
const user = inject("provideUser") //注入名,默认值
console.log("provideUser:", user.value)
</script>
<template>
<h3>我是底层的Nav组件</h3>
</template>
<style scoped></style>
运行结果:
【实验3】某个上层组件向所有下层组件广播函数,下层组件通过inject注入上层App组件广播的普通数据
在父组件定义一个方法 userAdd ,当父组件传值给所有子组件时,使用provide 将其进行注入
html
//父组件
<script setup>
import { provide, ref } from 'vue'
import Header from "./components/Header.vue"
//【实验3】某个上层组件向所有下层组件广播函数
const user = ref(0)
const userAdd = () => {
user.value++
}
provide("provideFuncUserAdd",userAdd) //注入名,值
</script>
<template>
<h3>我是上层的App组件, 用户数为: {{ user }}</h3>
<Header/>
</template>
<style scoped></style>
孙组件想要获取 provideFuncUserAdd 的值,只需使用inject 即可 ,下面组件并在按钮上绑定了 click 事件以执行由上层App组件注入的函数
html
//孙组件
<script setup>
import { inject } from 'vue'
//【实验3】下层组件通过inject注入上层App组件广播的函数
const funcUserAdd = inject("provideFuncUserAdd") //注入名,默认值
console.log("provideFuncUserAdd:", funcUserAdd)
</script>
<template>
<h3>我是底层的Nav组件</h3>
<button @click="funcUserAdd">添加用户</button>
</template>
<style scoped></style>
运行结果: