vue3 组件间如何进行通信?
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. 自定义事件
常用于:子 => 父。
原生事件:
事件名是特定的(click
、mosueenter
等等)事件对象$event
: 是包含事件相关信息的对象(pageX
、pageY
、target
、keyCode
)
自定义事件:
事件名是任意名称事件对象$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>
$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>
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>
对于原生事件, $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>
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>
还没有任何评论,你来说两句吧