vue3 组件间如何进行通信?

3个月前 (01-31) 0 点赞 0 收藏 0 评论 7 已阅读

Vue3组件通信和Vue2的区别:

移出事件总线,使用mitt代替。vuex换成了pinia。把.sync优化到了v-model里面了。把$listeners所有的东西,合并到$attrs中了。$children被砍掉了。

在这里插入图片描述

1. props

父传子:属性值是非函数。若 子传父:属性值是函数

父组件:

<template>  <div class="father">    <h3>--父组件,</h3>    <h4>我的车:{{ car }}</h4>    <h4>儿子给的玩具:{{ toy }}</h4>    <Son      :car="car"      :getSonToy="getSonToy"    />  </div></template><script setup lang="ts">import Son from './Son.vue'import { ref } from "vue";// 数据const car = ref('奔驰')const toy = ref()// 方法function getSonToy(value: string) {  toy.value = value}</script>

子组件:

<template>  <div class="child">    <h3>--子组件--</h3>    <h4>我的玩具:{{ toy }}</h4>    <h4>父给我的车:{{ car }}</h4>    <button @click="getSonToy(toy)">玩具给父亲</button>  </div></template><script setup lang="ts">import { ref } from "vue";const toy = ref('奥特曼')defineProps(['car', 'getSonToy'])</script>
在这里插入图片描述

2. 自定义事件

常用于:子 => 父

原生事件:

事件名是特定的(clickmosueenter等等)事件对象$event: 是包含事件相关信息的对象(pageXpageYtargetkeyCode

自定义事件:

事件名是任意名称事件对象$event: 是调用emit时所提供的数据,可以是任意类型

父组件:

<template>  <div class="father">    <h3>--父组件,</h3>    <h4>我的车:{{ car }}</h4>    <h4>儿子给的玩具:{{ toy }}</h4>    <Son      @get-son-toy="getSonToy"    />  </div></template><script setup lang="ts">import Son from './Son.vue'import { ref } from "vue";// 数据const car = ref('奔驰')const toy = ref()// 方法function getSonToy(value: string) {  toy.value = value}</script>

子组件:

<template>  <div class="child">    <h3>--子组件--</h3>    <h4>我的玩具:{{ toy }}</h4>    <!-- <h4>父给我的车:{{ car }}</h4> -->    <button @click="emit('get-son-toy', toy)">玩具给父亲</button>  </div></template><script setup lang="ts">import { ref } from "vue";const toy = ref('奥特曼')const emit = defineEmits(['get-son-to:y'])</script>
在这里插入图片描述

扩展一个知识点:$event

<template>  <div class="child">    <h3>--子组件--</h3>    <h4>我的玩具:{{ toy }}</h4>    <!-- <h4>父给我的车:{{ car }}</h4> -->    <!-- <button @click="emit('get-son-toy', toy)">玩具给父亲</button> -->    <button @click="test('1', $event)">test</button>  </div></template><script setup lang="ts">import { ref } from "vue";const toy = ref('奥特曼')// const emit = defineEmits(['get-son-toy'])const test = (a: string, e: Event) => {  console.log(a, e);}</script>
image.png

$event 为事件对象。

3. mitt

任意组件之间通信。

emitter.ts

// 引入mitt import mitt from "mitt";// 创建emitterconst emitter = mitt()/*  // 绑定事件  emitter.on('abc',(value)=>{    console.log('abc事件被触发',value)  })  emitter.on('xyz',(value)=>{    console.log('xyz事件被触发',value)  })  setInterval(() => {    // 触发事件    emitter.emit('abc',666)    emitter.emit('xyz',777)  }, 1000);  setTimeout(() => {    // 清理事件    emitter.all.clear()  }, 3000); */// 创建并暴露mittexport default emitter

子组件:

<template>  <div class="child">    <h3>--子组件--</h3>    <h4>我的玩具:{{ toy }}</h4>    <button @click="emitter.emit('send-toy', toy)">玩具给父亲</button>  </div></template><script setup lang="ts">import { ref } from "vue";import emitter from '../utils/emitter'const toy = ref('奥特曼')</script>

父组件:

<template>  <div class="father">    <h3>--父组件,</h3>    <h4>我的车:{{ car }}</h4>    <h4>儿子给的玩具:{{ toy }}</h4>    <Son/>  </div></template><script setup lang="ts">import Son from './Son.vue'import { ref, onUnmounted } from "vue";import emitter from '../utils/emitter'// 数据const car = ref('奔驰')const toy = ref()// 绑定事件emitter.on('send-toy', (data) => {  toy.value = data  console.log('send-toy事件被触发', data)})onUnmounted(() => {  // 解绑事件  emitter.off('send-toy')})</script>
image.png

4. v-model

组件库底层父子传参常用。

MyInput 组件:

<template>  <div class="box">    <input      type="text"      :value="modelValue"      @input="emit('update:modelValue', (<HTMLInputElement>$event.target).value)"    >  </div></template><script setup lang="ts">// 接收propsdefineProps(['modelValue'])// 声明事件const emit = defineEmits(['update:modelValue'])</script>

父组件:

<template>  <div class="father">    <h3>--父组件--</h3>    <MyInput v-model="username"/>    <!-- 原理: -->    <!-- <MyInput :modelValue="username" @update:modelValue="username = $event"/> -->  </div></template><script setup lang="ts">import MyInput from './MyInput.vue'import {ref} from 'vue'const username = ref('name')</script>
image.png

对于原生事件, $event就是事件对象=====>能使用.target对于自定义事件,$event就是触发事件时,所传递的数据==>不能.target

此外,还可以进行部分自定义修改:

父组件:

<MyInput v-model:myname="username"/>

MyInput 组件:

<template>  <div class="box">    <input      type="text"      :value="myname"      @input="emit('update:myname', (<HTMLInputElement>$event.target).value)"    >  </div></template><script setup lang="ts">// 接收propsdefineProps(['myname'])// 声明事件const emit = defineEmits(['update:myname'])</script>

5. $attrs

用于祖孙传参。

具体说明:$attrs是一个对象,包含所有父组件传入的标签属性。

$attrs会自动排除props中声明的属性

Father.vue

<template>  <div class="father">    <h3>--Father--</h3>    <div>a: {{ a }}</div>    <div>b: {{ b }}</div>    <Son :a="a" :b="b" :addA="addA"  v-bind="{x: 100, y: 200}" ></Son>  </div></template><script setup lang="ts">import {ref} from 'vue'import Son from './Son.vue'const a = ref(1)const b = ref(2)const addA = (data: number)=> {  a.value += data}</script>

Son.vue

<template>  <div class="box">    <h3>--Son--</h3>    <div>a: {{ a }}</div>    <GrandSon v-bind="$attrs"/>  </div></template><script setup lang="ts">import GrandSon from './GrandSon.vue';defineProps(['a'])</script>

GrandSon.vue

<template>  <div class="box">    <h3>--GrandSon--</h3>    <div>b: {{ b }}</div>    <div>x: {{ x }}</div>    <div>y: {{ y }}</div>    <button @click="addA(1)">点我将a加1</button>  </div></template><script setup lang="ts">defineProps(['b', 'x', 'y' ,'addA'])</script>
image.png
image.png

6. $refs$parent

概述:

$refs用于 :父→子。$parent用于:子→父。

原理如下:

属性 说明

$refs值为对象,包含所有被ref属性标识的DOM元素或组件实例。$parent值为对象,当前组件的父组件实例对象。

Father.vue

<template> <div class="father">  <h3>父组件</h3>  <h4>房产:{{ house }}</h4>  <button @click="changeToy">修改Child1的玩具</button>  <button @click="changeComputer">修改Child2的电脑</button>  <button @click="getAllChild($refs)">让所有孩子的书变多</button>  <Child1 ref="c1" />  <Child2 ref="c2" /> </div></template><script setup lang="ts" name="Father">import Child1 from './Child1.vue'import Child2 from './Child2.vue'import { ref, reactive } from "vue";let c1 = ref()let c2 = ref()// 注意点:当访问obj.c的时候,底层会自动读取value属性,因为c是在obj这个响应式对象中的// 解释了为什么refs[key].book等写法后面不加.value/* let obj = reactive({ a:1, b:2, c:ref(3)})let x = ref(4)console.log(obj.a)console.log(obj.b)console.log(obj.c)console.log(x) */// 数据let house = ref(4)// 方法function changeToy() { c1.value.toy = '小猪佩奇'}function changeComputer() { c2.value.computer = '华为'}function getAllChild(refs: { [key: string]: any }) { console.log(refs) for (let key in refs) {  refs[key].book += 3 }}// 向外部提供数据defineExpose({ house })</script><style scoped>.father { background-color: rgb(165, 164, 164); padding: 20px; border-radius: 10px;}.father button { margin-bottom: 10px; margin-left: 10px;}</style>

Child1.vue

<template> <div class="child1">  <h3>子组件1</h3>  <h4>玩具:{{ toy }}</h4>  <h4>书籍:{{ book }} 本</h4>  <button @click="minusHouse($parent)">干掉父亲的一套房产</button> </div></template><script setup lang="ts" name="Child1">import { ref } from "vue";// 数据let toy = ref('奥特曼')let book = ref(3)// 方法function minusHouse(parent: any) { parent.house -= 1}// 把数据交给外部defineExpose({ toy, book })</script><style scoped>.child1 { margin-top: 20px; background-color: skyblue; padding: 20px; border-radius: 10px; box-shadow: 0 0 10px black;}</style>

Child2.vue

<template> <div class="child2">  <h3>子组件2</h3>  <h4>电脑:{{ computer }}</h4>  <h4>书籍:{{ book }} 本</h4> </div></template><script setup lang="ts" name="Child2">import { ref } from "vue";// 数据let computer = ref('联想')let book = ref(6)// 把数据交给外部defineExpose({ computer, book })</script><style scoped>.child2 { margin-top: 20px; background-color: orange; padding: 20px; border-radius: 10px; box-shadow: 0 0 10px black;}</style>

7. provide、inject

实现祖孙组件直接通信。

在祖先组件中通过provide配置向后代组件提供数据在后代组件中通过inject配置来声明接收数据

Father 组件:

<template>  <div class="father">    <h3>父组件</h3>    <h4>银子:{{ money }}万元</h4>    <h4>车子:一辆{{car.brand}}车,价值{{car.price}}万元</h4>    <Child/>  </div></template><script setup lang="ts" name="Father">  import Child from './Child.vue'  import {ref,reactive,provide} from 'vue'  let money = ref(100)  let car = reactive({    brand:'奔驰',    price:100  })  function updateMoney(value:number){    money.value -= value  }  // 向后代提供数据  provide('moneyContext',{money,updateMoney})  provide('car',car)</script><style scoped>  .father {    background-color: rgb(165, 164, 164);    padding: 20px;    border-radius: 10px;  }</style>

GrandChild 组件:

<template>  <div class="grand-child">    <h3>我是孙组件</h3>    <h4>银子:{{ money }}</h4>    <h4>车子:一辆{{car.brand}}车,价值{{car.price}}万元</h4>    <button @click="updateMoney(6)">花爷爷的钱</button>  </div></template><script setup lang="ts" name="GrandChild">  import { inject } from "vue";  let {money,updateMoney} = inject('moneyContext',{money:0,updateMoney:(param:number)=>{}})  let car = inject('car',{brand:'未知',price:0})</script><style scoped>  .grand-child{    background-color: orange;    padding: 20px;    border-radius: 10px;    box-shadow: 0 0 10px black;  }</style>

8. pinia

9. slot


vue3 组件间如何进行通信?

本文收录在
0评论

登录

忘记密码 ?

切换登录

注册