目录
vue3组件通信方式总结
1.props(父传子)
父组件给子组件传值,子组件通过defineProps获取父组件传递的数据。
父组件
<template>
<Child info="小心" :money="money"></Child>
</template>
<script setup lang="ts">
import Child from "./Child.vue";
import {
ref } from "vue";
let money = ref(10000);
</script>
子组件
<template>
<p>{
{
info }}</p>
<p>{
{
money }}</p>
</template>
<script setup lang="ts">
//使用defineProps方法接受父组件传递过来的数据
const props = defineProps(["info", "money"]); //数组写法
// const props = defineProps({ //对象写法
// info: String,
// money: Number,
// });
</script>
- 注:vue2中的写法
//props:['name'] //方式1
//props:{name:String} //方式2
props:{
//方式3
name:{
type:String, //类型
required:true, //必要性
default:'老王' //默认值
}
}
2.emit(子传父)
通过自定义事件实现子传父,子组件中通过emit触发自定义事件。
父组件
<template>
<!-- 绑定自定义事件xxx:实现子组件给父组件传递数据 -->
<Child @xxx="handler"></Child>
</template>
<script setup lang="ts">
import Child from "./Child.vue";
const handler = (param1: any, param2: any) => {
console.log(param1, param2); //小,心
};
</script>
子组件
<template>
<button @click="handler">子传父</button>
</template>
<script setup lang="ts">
//利用defineEmits方法返回函数触发自定义事件
let emit = defineEmits(["xxx"]);
const handler = () => {
emit("xxx", "小", "心");
};
</script>
- 注:vue2中的写法,直接通过vue实例对象进行触发
this.$emit('xxx',数据);
3.mitt插件(全局事件总线)
全局事件总线可以实现任意组件通信,Vue 3 中移除了 eventBus,但可以借助使用mitt插件来实现。
用法参考mitt官网
安装
npm install --save mitt
使用
通过emit方法分发自定义事件,on方法绑定自定义事件监听。
- 注:vue2中写法
new Vue({
......
beforeCreate() {
Vue.prototype.$bus = this //安装全局事件总线
},
......
})
this.$bus.$on('xxxx',this.demo) //需要接受数据的组件中给$bus绑定自定义事件
this.$bus.$emit('xxxx',数据) //另一个组件中提供数据
4.v-model(父子组件数据同步)
v-model可以实现表单数据数据的双向绑定。
此外,v-model也可以实现父子组件数据同步,类似于vue2中xxxx.sync这种形式的语法糖。
需求:点击按钮,父子组件数据同步改变。
- 方式1:使用props和emit实现父子组件同步
父组件:传给子组件money数据,并在自定义事件update:modelValue中接受子组件传递的money更新数据。
子组件:通过props接受父组件传递的money,并通过emit触发自定义事件,更新父组件数据。
- 方式2:使用v-model简化
v-model实质是利用props[modelValue]与自定义事件[update:modelValue]实现的。相当于给子组件Child传递一个props[modelValue]与绑定一个自定义事件update:modelValue
可以通过开发者工具看到
同时可以通过使用多个v-model,实现父子组件多个数据的同步。
<!-- 相当于给组件Child传递两个props分别是pageOne与pageTwo,以及绑定两个自定义事件update:pageOne与update:pageTwo实现父子数据同步 -->
<Child v-model:pageOne="msg1" v-model:pageTwo="msg2"></Child>
补充:elementPlus中的分页也使用了v-model这种写法。
- 注:vue2中.async用法
:money.async代表父组件给子组件传递字符串props[money],给当前子组件绑定一个自定义事件(update:money)
<template>
<Money :money.sync="money"/>
<!--等价于 -->
<Money :money="money" @update:money="money=$event">
</template>
5.useAttrs(父传子)
Vue3中可以利用useAttrs方法获取父组件的属性与事件(包含原生DOM事件和自定义事件)。类似于props,可以接受父组件传递过来的属性与属性值。
案例:使用el-button封装一个组件,使其具有提示功能。
父组件
子组件
通过useAttrs
方法获取组件上的属性与事件。
此外,el-button上可以采用type=”$attrs.type“ size="$attrs.size"......
的写法,但这样过于繁琐,如果传递的属性名和绑定标签的属性名是一致的,直接使用:="$attrs"的形式。
<template>
<div :title="title">
<el-button :="$attrs"></el-button>
</div>
</template>
<script setup lang="ts">
//引入useAttrs方法:获取组件标签身上属性与事件
import {
useAttrs } from "vue";
let $attrs = useAttrs();
let props = defineProps(["title"]);
console.log($attrs);
</script>
- 注意点:
如果defineProps接受了某一个属性,useAttrs方法返回的对象身上就没有相应属性与属性值。这里已经用props接受title,useAttrs方法就获取不到title。
- 注:Vue2中写法:使用
$attrs
属性与$listeners
$attrs
是组件实例的一个属性,可以获取父组件传递过来的props数据;$listeners
是组件实例的属性,获取父组件给子组件传递的自定义事件;
同样,如果子组件通过props接收的属性,在$attrs属性当中是获取不到的。
父组件:
<template>
<ElButton type="success" icon="el-icon-delete" size="mini" title="按钮" @click="handler"/>
</template>
子组件
<template>
<a :title="title">
<el-button v-bind="$attrs" v-on="$listeners"></el-button>
</a>
</template>
<script>
export default {
props:['title'],
mounted(){
console.log(this.$attrs);
}
}
</script>
6.ref/$parent(父子组件通信)
6.1 ref
ref 用于注册元素或子组件的引用,即可以获取DOM元素和子组件实例。
那么就可以通过ref实现子传父,在父组件中使用子组件的数据和方法。
- 1.当使用选项式 API,引用将被注册在组件的 this.$refs 对象里:
<!-- 存储为 this.$refs.p -->
<p ref="p">hello</p>
- 2.当使用组合式 API,引用将存储在与名字匹配的 ref 里:
如果用于普通 DOM 元素,引用将是元素本身;如果用于子组件,引用将是子组件的实例。
- 注意点:
vue3中使用 script setup 的组件是默认关闭的,外部不能访问,如果想让父组件获取子组件的数据或者方法,需要在子组件中通过defineExpose来指定需要对外暴露的属性。
例:
父组件
子组件
6.2 $parent
$parent可以获取当前组件的父组件实例,因此可以在子组件中获取父组件的数据和方法。
父组件。父组件的数据与方法需要通过defineExpose方法对外暴露
<template>
<Child></Child>
</template>
<script setup lang="ts">
import Child from "./Child.vue";
import {
ref } from "vue";
let yourMoney = ref(666);
let myMoney = ref(10);
defineExpose({
yourMoney,
myMoney,
});
</script>
子组件。子组件内部按钮点击时获取父组件实例。
<template>
<div class="dau">
<button @click="handler($parent)">点击获取父组件实例</button>
</div>
</template>
<script setup lang="ts">
const handler = ($parent) => {
console.log($parent.yourMoney); //666
console.log($parent.myMoney); //10
};
</script>
- 注:vue2中写法
vue2中还能借助$children
,来获取到当前组件的所有子组件的全部实例。
vue3中$children
被移除了,被$refs替代。
7.provide/inject(祖先后代组件通信)
vue3提供两个方法provide与inject,无论层级多深,API 都可以实现父组件到子孙组件的数据传递。
7.1 provide(提供)
provide用于提供可以被后代组件注入的值。
父组件
<script setup lang="ts">
import Child from "./Child.vue";
import {
ref, provide } from "vue";
let car = ref("小星星");
//祖先组件给后代组件提供数据
//第一个参数就是提供的数据key
//第二个参数是提供的数据
provide("TOKEN", star);
</script>
7.2 inject(注入)
后代组件可以通过inject(注入)方法获取数据,通过key获取存储的数值。
子组件
<template>
<div class="child1">
<h1>孙子组件</h1>
<p>{
{
star }}</p>
<button @click="updateCar">更新数据</button>
</div>
</template>
<script setup lang="ts">
import {
inject } from "vue";
//注入祖先组件提供数据
//需要参数:即为祖先提供数据的key
let star = inject("TOKEN");
console.log(star.value); //小星星
//后代组件可以修改注入的值,祖先组件提供的值也会跟着变
const updateCar = () => {
star.value = "摘下月亮";
};
</script>
</script>
注意:后代组件可以修改注入的值,祖先组件提供的值也会跟着变。
效果
8.slot(父子组件通信)
插槽:分为默认插槽、具名插槽、作用域插槽,可以实现父子组件通信。
8.1默认插槽
在子组件内部使用slot全局组件标签
<template>
<div>
<slot></slot>
</div>
</template>
<script setup lang="ts">
</script>
<style scoped>
</style>
父组件。Todo为子组件,双标签内部书写结构传递给子组件。
<Todo>
<h1>我是默认插槽填充的结构</h1>
</Todo>
8.2具名插槽
带有名字在组件内部留多个指定名字的插槽
子组件,有两个具名插槽
<template>
<div>
<h1>todo</h1>
<slot name="a"></slot>
<slot name="b"></slot>
</div>
</template>
<script setup lang="ts">
</script>
<style scoped>
</style>
父组件
父组件内部向指定的具名插槽传递结构。使用v-slot:,可以替换为#。
<template>
<div>
<h1>slot</h1>
<Todo>
<template v-slot:a> //可以用#a替换
<div>填入组件A部分的结构</div>
</template>
<template #b>//可以用#b替换
<div>填入组件B部分的结构</div>
</template>
</Todo>
</div>
</template>
<script setup lang="ts">
import Todo from "./Todo.vue";
</script>
<style scoped>
</style>
8.3作用域插槽
作用域插槽:就是可以传递数据的插槽,子组件可以将数据传给父组件,父组件可以决定这些回传的数据是以何种结构或者外观在子组件内部去展示。
以下例子中父组件把todos数据传给子组件,子组件接受父组件的值,并通过作用域插槽将数据回传给了父组件,父组件中来给数据设定样式。
父组件
子组件
效果
- 注:vue2中父组件通过scope属性值获取子组件的数据
<template scope="scopeData">
<ul>
<li v-for="g in scopeData.games" :key="g">{
{g}}</li>
</ul>
</template>
9.pinia/vuex(任意组件通信)
vue3可以使用Pinia和Vuex4这两种集中式管理状态容器来实现任意组件通信。
- vuex核心概念:state、mutations、actions、getters、modules
- pinia核心概念:state、actions、getters。没有mutation、modules。
- 注意:vue2中使用Vuex3,也可以使用Pinia。