vue3 组件通信

624 阅读4分钟

一、【props】

props通信方式是实际开发当中运用比较多的通信方式,简化 父传子,子传父
若:父传子通过属性绑定非函数方式;若:子传父通过属性绑定函数方式

1.父传子

父组件
<template>
    <div class="father">
        <p>我是父组件</p>
        <p>
            父组件接受子组件的数据:{{  }}
        </p>
        <Son :money="money"/>
    </div>
</template>
<script lang="ts" setup name="Father">
import { ref } from 'vue'
import Son from './son.vue'
const money = ref(100)
</script>
<style>
.father{
    width: 100%;
    height: 200px;
    background-color: aquamarine;
}
</style>
子组件
<template>
    <div class="son">
        <p>我是子组件</p>
        <p>父组件给我的数据:{{ money }}</p>
    </div>
</template>
<script lang="ts" setup name="Son">
    defineProps(['money'])
</script>

2.子传父

父组件
<template>
    <div class="father">
        <p>我是父组件</p>
        <p>
            父组件接受子组件的数据:{{ sonMoney }}
        </p>
        <Son :money="money" :sonSendMoeny="getMoeny"/>
    </div>
</template>
<script lang="ts" setup name="Father">
import { ref } from 'vue'
import Son from './son.vue'
// 父亲的钱
const money = ref(100)
// 儿子给父亲的钱
const sonMoney = ref(0)
// 父组件传递给子组件的函数
function getMoeny(value:number){
    sonMoney.value = value
}
</script>
<style>
.father{
    width: 100%;
    height: 200px;
    background-color: aquamarine;
}
</style>
子组件
<template>
    <div class="son">
        <p>我是子组件</p>
        <p>父组件给我的数据:{{ money }}</p>
        <button @click="sonSendMoeny(senFatherMoney)">儿子给父亲的钱</button>
    </div>
</template>
<script lang="ts" setup name="Son">
import { ref } from 'vue'
    defineProps(['money','sonSendMoeny'])
    // 儿子给父亲的钱
    const senFatherMoney = ref(50)
</script>

二、自定义事件子传父

父组件
<template>
    <div class="father">
        <p>我是父组件</p>
        <p>
            父组件接受子组件的数据:{{ sonMoney }}
        </p>
        <Son :money="money" @son-send-moeny="getMoeny"/>
    </div>
</template>
<script lang="ts" setup name="Father">
import { ref } from 'vue'
import Son from './son.vue'
// 父亲的钱
const money = ref(100)
// 儿子给父亲的钱
const sonMoney = ref(0)
// 父组件传递给子组件的函数
function getMoeny(value:number){
    sonMoney.value = value
}
</script>
<style>
.father{
    width: 100%;
    height: 200px;
    background-color: aquamarine;
}
</style>
子组件
    <template>
    <div class="son">
        <p>我是子组件</p>
        <p>父组件给我的数据:{{ money }}</p>
        <button @click="sonSendMoeny">儿子给父亲的钱</button>
    </div>
</template>
<script lang="ts" setup name="Son">
import { ref } from 'vue'
    defineProps(['money'])
    const emit = defineEmits(['son-send-moeny'])
    // 儿子给父亲的钱
    const senFatherMoney = ref(50)
    // 子组件触发的函数
    function sonSendMoeny(){
        emit('son-send-moeny',senFatherMoney)
    }
</script>

三、发布订阅方式(事件总线)mitt 实现任意组件通信

mitt 的用法:
1. npm i mitt 安装miit
2.导入 调用  暴露
// 引入mitt
import mitt from 'mitt'
// 调用mitt得到emitter  emitter能绑定事件 触发事件
const emitter = mitt()
// 暴露事件
export default emitter
// 用法
// emitter.on('事件名称','回调') 绑定事件
// emitter.emit('事件名称')  触发事件
// emitter.off('事件名称')  解除绑定事件
// emitter.all.clear() 解除所有绑定事件

Snipaste_2024-08-11_16-43-38.png 哥哥组件

<template>
    <div class="son">
        <p>我是哥哥组件</p>
        <p>妹妹给我的钱:{{ GirlMoney }}</p>
        <button @click="giveMoeny">哥哥给妹妹的钱</button>
    </div>
</template>
<script lang="ts" setup name="Son">
import { ref } from 'vue'
import emitter from '@/untils/emitter';
    const money = ref(100)
    // 妹妹给我钱
    const GirlMoney = ref(0)
    // 哥哥触发事件给妹妹钱
    function giveMoeny(){
        emitter.emit('gg-send-money',money)
    }
    // 哥哥订阅妹妹触发的事件
    emitter.on('mm-send-money',(value:any)=>{
        GirlMoney.value = value
    })
  onUnmounted(()=>{
    emitter.off('mm-send-money')
})
</script>

Snipaste_2024-08-11_16-45-51.png 妹妹组件

<template>
    <div class="girl">
        哥哥给我{{ brotherMoney }}块
    </div>
    <button @click="getMoeny">妹妹给哥哥钱</button>
</template>
<script lang="ts" setup name="Girl">
import { ref } from 'vue'
import emitter from '@/untils/emitter';
const money = ref(50)
const brotherMoney = ref(0)
// 妹妹订阅哥哥处触发的事件
emitter.on("gg-send-money",(value:any)=>{
        brotherMoney.value = value
        console.log(value)
    })
// 妹妹触发事件给哥哥钱
 function getMoeny(){
    emitter.emit('mm-send-money',money)
}
onUnmounted(()=>{
    emitter.off('gg-send-money')
})
</script>

Snipaste_2024-08-11_16-46-56.png 注意:组件在卸载的时候,一定要把注册的事件卸载了

四、【$attrs】 组件通信方式

1.$attrs用于实现当前组件的父组件与孙子组件的通信(父->子->孙)
2.$attrs是一个对象,包含所有父组件的标签属性

爷爷组件

<template>
    <div class="father">
        <p>父亲的数据</p>
        <h1>{{ a }}</h1>
        <h1>{{ b }}</h1>
        <h1>{{ c }}</h1>
        <h1>{{ d }}</h1>
        <Son :a="a" :b="b" :c="c" :d="d" :changeData="changeData"/>
    </div>
</template>
<script lang="ts" setup name="Father">
import { ref } from 'vue'
import Son from './son.vue'
let a = ref(1)
let b = ref(2)
let c = ref(3)
let d = ref(4)
// 孙子改变爷爷的数据
function changeData(data:number){
    a.value += data
    b.value += data
}
</script>
<style>
.father{
    width: 100%;
    height: auto;
    background-color: aquamarine;
    padding-bottom: 30px;
}
</style>

Snipaste_2024-08-11_20-54-19.png

父亲组件

<template>
    <div class="son">
        <Grandson v-bind="$attrs"/>
    </div>
    
</template>
<script lant="ts" setup name="Son">
import Grandson from './grandson.vue';
</script>

Snipaste_2024-08-11_20-55-17.png

孙子组件

<template>
    <div class="grandson">
        <p>爷爷给的数据:</p>
        <h1>{{ a }}</h1>
        <h1>{{ b }}</h1>
        <h1>{{ c }}</h1>
        <h1>{{ d }}</h1>
        <button @click="changeData(2)">孙子改变爷爷的数据</button>
    </div>
</template>
<script lant="ts" setup name="Son">
defineProps(['a','b','c','d','changeData'])
</script>

Snipaste_2024-08-11_20-55-17.png

五 【refsrefs、parent】父子组件通信

1.【$refs】父组件向子组件传递数据

父组件代码

<template>
    <div class="father">
        <p>父亲有{{money}}块钱</p>
        <son1 ref="son1"/>
        <son2 ref="son2"/>
        <button @click="sendSon">父亲给儿子书和玩具</button>
        <button @click="senAllSon($refs)">同时操作所有儿子</button>
    </div>
</template>
<script lang="ts" setup name="Father">
import { ref } from 'vue'
import Son1 from './son1.vue'
import Son2 from './son2.vue'
let money = ref(500)
let son1 = ref()
let son2 = ref()
function sendSon(){
    son1.value.bookName = '宇宙简史'
    son1.value.toyName = '灭霸'
    son2.value.bookName = '红楼梦'
    son2.value.bookName = '孙悟空'
}
function senAllSon(refs:{[key:string]:any}){
    for(let key in refs){
        refs[key].bookName = '孙子兵法'
        refs[key].toyName = '猪八戒'
        console.log(key)
    }
}
</script>
<style>
.father{
    width: 100%;
    height: auto;
    background-color: aquamarine;
    padding-bottom: 30px;
}
</style>
子组件son1代码
<template>
    <div class="son1">
        son1的book:{{ bookName }}
        son1的toy:{{ toyName }}
    </div>
    
</template>
<script lant="ts" setup name="Son1">
import { ref } from 'vue'
    let bookName = ref('美国近代史')
    let toyName = ref('蜘蛛侠')
    // 向外暴露数据
    defineExpose({bookName,toyName})
</script>
子组件son2代码
<template>
    <div class="son">
        son2的book:{{ bookName }}
        son2的toy:{{ toyName }}
    </div>
    
</template>
<script lant="ts" setup name="Son2">
import { ref } from 'vue'
    let bookName = ref('中国近代史')
    let toyName = ref('奥特曼')
    // 向外暴露数据
    defineExpose({bookName,toyName})
</script>

1.【$parent】子组件向父组件传递数据

父组件代码

<template>
    <div class="father">
        <p>父亲有{{money}}块钱</p>
        <son2 ref="son2"/>
    </div>
</template>
<script lang="ts" setup name="Father">
import { ref } from 'vue'
import Son2 from './son2.vue'
let son2 = ref()
let money = ref(500)
// 向外暴漏数据
defineExpose({money})
</script>
<style>
.father{
    width: 100%;
    height: auto;
    background-color: aquamarine;
    padding-bottom: 30px;
}
</style>

子组件代码

<template>
    <div class="son">
        <button @click="senMoney($parent)">子组件向父组件给钱</button>
    </div>
    
</template>
<script lant="ts" setup name="Son2">
import { ref } from 'vue'
function senMoney(parent){
    parent.money = 5000
}
</script>

六、provide、inject隔代通信

1.祖传子代

爷爷组件代码:
<template>
    <div class="father">
        {{ house.name }}---{{ house.price }}
        
        <Son />
    </div>
</template>
<script lang="ts" setup name="Father">
import { ref,reactive,provide } from 'vue'
import Son from './son.vue'
let house = reactive({
    name:'别墅',
    price:500
})
let money = ref(1000)
provide('house',house)
</script>
<style>
.father{
    width: 100%;
    height: auto;
    background-color: aquamarine;
    padding-bottom: 30px;
}
</style>
儿子组件代码:
<template>
    <div class="son">
        <grandSon />
    </div>
    
</template>
<script lant="ts" setup name="Son">
import grandSon from './grandSon.vue';
</script>
孙子组件代码:
<template>
    <div class="son">
        爷爷给我的房子:{{ house.name }}
    </div>
    
</template>
<script lant="ts" setup name="Son2">
import { inject } from 'vue'
// inject 第一个参数 你要传递的数据,第二个参数  默认值
let house = inject('house',{name:'房子',price:600})
console.log(house)
</script>

2.孙子组件改变爷爷组件的数据

爷爷组件通过provide inject 传递一个函数到孙子组件  在孙子组件调用这个函数,就可以实现隔代孙子组件改变爷爷组件的数据
爷爷组件代码:
<template>
    <div class="father">
        {{ house.name }}---{{ house.price }}
        {{ money }}
        <Son />
    </div>
</template>
<script lang="ts" setup name="Father">
import { ref,reactive,provide } from 'vue'
import Son from './son.vue'
let house = reactive({
    name:'别墅',
    price:500
})
let money = ref(1000)
function updataMoney(value:number){
    money.value -= value
}
provide('house',house)
provide('updataMoney',updataMoney)
</script>
<style>
.father{
    width: 100%;
    height: auto;
    background-color: aquamarine;
    padding-bottom: 30px;
}
</style>
儿子组件代码:
<template>
    <div class="son">
        <grandSon />
    </div>
    
</template>
<script lant="ts" setup name="Son">
import grandSon from './grandSon.vue';
</script>
孙子组件代码:
<template>
    <div class="son">
        爷爷给我的房子:{{ house.name }}
        <button @click="updataMoney(6)">我花爷爷的钱</button>
    </div>
    
</template>
<script lant="ts" setup name="Son2">
import { inject } from 'vue'
// inject 第一个参数 你要传递的数据,第二个参数  默认值
let house = inject('house',{name:'房子',price:600})
let updataMoney = inject('updataMoney',()=>{})
console.log(house)
</script>

****注:provide inject 不仅实现祖孙通信,也可以实现祖徒孙通信,它的核心 是隔代通信 可以隔很多代!!!!!!!