Vue3组件通信方式
- 方式一:props
- 方式二:自定义事件
- 方式三:mitt
- 方式四:v-model
- 方式五:$attrs
- 方式六:refs, parent
- 方式七:provide, inject
总结来源于 尚硅谷vue3
大部分实现双向通信都是向子组件传递一个函数,在函数中修改。总结写在前头:
- 任意间组件通信: mitt
- 若想实现父子间双向通信
- props
- v-model
- provide, inject
- refs, parent组合使用,不如上面的方便
- 若实现单向通信
- 子到父
- 自定义事件
- parent
- 父到子
- refs
- 祖-> 孙
- provide, inject
- $attrs, 需要父组件参与且父组件一旦获取某个值,一般情况下子组件不能通过这种方式再获取了
- 子到父
方式一:props
Props可以实现 父->子, 子->父
- 若 父传子:属性值是非函数。
- 若 子传父:属性值是函数。
使用到的红函数: defineProps([‘xx’])
方式二:自定义事件
可以实现子传父
- 注意区分好:原生事件、自定义事件。
- 原生事件:
- 事件名是特定的(
click
、mouseenter
等等)- 事件对象
$event
: 是包含事件相关信息的对象(pageX
、pageY
、target
、keyCode
)- 自定义事件:
- 事件名是任意名称
- 事件对象
$event
: 是调用emit
时所提供的数据,可以是任意类型!!!
demo
<Child @xxx = “function / 表达式”/>
这里相当于在Child组件上绑定了一个事件xxx
,子组件可以通过:
const emits = defineEmits(['xx'])
获取自定的事件.然后 emits('xx', 相关数据)
将数据传到回调函数function中
方式三:mitt
可以实现任意组件间的通信
可以理解成一个第三方自定义事件仓库(自己向里面放)
- 下载mitt,打开终端(vscode中按ctrl + ~), npm i mitt
- 在src文件下载新建工具包utils, 新建
emitter.ts
文件 - 创建mitt
// 引入mitt
import mitt from "mitt";// 创建emitter
const emitter = mitt()// 创建并暴露mitt
export default emitter
- 使用
// 绑定事件
emitter.on('send-toy',(value)=>{console.log('send-toy事件被触发',value)
})onUnmounted(()=>{// 解绑事件emitter.off('send-toy')
})// 需要传数据的vue文件中写如下代码
function sendToy(){// 触发事件emitter.emit('send-toy',toy.value)
}
Notice:记得在组件销毁前解绑相关事件
方式四:v-model
v-model放在组件标签上也可以时间父子间双向通信,一般组件库常用这种方法
<!--
子组件是一个自定义的input组件,里面放着自己写的input结构和样式
-->
<Child v-model="username"/><!-- 上面情况等价于 -->
<Child :username="username"
@update:modelValue="username=$emit" />
Chile.vue
<template><div class="box"><!--将接收的value值赋给input元素的value属性,目的是:为了呈现数据 --><!--给input元素绑定原生input事件,触发input事件时,进而触发update:model-value事件--><input type="text" :value="modelValue" @input="emit('update:model-value',$event.target.value)"></div>
</template><script setup lang="ts" name="AtguiguInput">// 接收propsdefineProps(['modelValue'])// 声明事件const emit = defineEmits(['update:model-value'])
</script>
如果不想使用modelValue这个标识符,可以这样:v-model:xx
,这里的xx就代替了modelValue, 这种情况下你可以传递多个值
方式五:$attrs
实现 祖->孙 数据传递
本质上就是祖->父, 父->子,只不过这个过程父亲不能接受数据
祖
v-bind="{x:100,y:200} === :x=“100” :y=“200”
<template><div class="father"><h3>父组件</h3><Child :a="a" :b="b" :c="c" :d="d" v-bind="{x:100,y:200}" :updateA="updateA"/></div>
</template>
父
<h3>子组件</h3>
<!-- 祖传的数据父亲不用,都存在$attrs上了,可以借助vue开发者工具查看 -->
<GrandChild v-bind="$attrs"/>
孙
<template><div class="grand-child"><h3>孙组件</h3><h4>a:{{ a }}</h4><h4>b:{{ b }}</h4><h4>c:{{ c }}</h4><h4>d:{{ d }}</h4><h4>x:{{ x }}</h4><h4>y:{{ y }}</h4><button @click="updateA(666)">点我更新A</button></div>
</template><script setup lang="ts" name="GrandChild">defineProps(['a','b','c','d','x','y','updateA'])
</script>
方式六:refs, parent
可以实现父子间通信
$refs
获取所有ref标识的子组件的信息(包括数据),前提是子组件通过defineExpose将数据暴露出去$parent
获取当前组件的所有父组件的信息(包括数据),前提是父组件通过defineExpose将数据暴露出去
$refs | 值为对象,包含所有被ref 属性标识的DOM 元素或组件实例。 |
---|---|
$parent | 值为对象,当前组件的父组件实例对象。 |
方式七:provide, inject
实现祖<->孙间通信
- 不需要经过父组件的插手
- 在祖先组件中通过
provide
配置向后代组件(不一定只是孙, 父,曾孙等都可以)提供数据- 在后代组件中通过
inject
配置来声明接收数据
祖
<template><div class="father"><h3>父组件</h3><h4>资产:{{ money }}</h4><h4>汽车:{{ car }}</h4><button @click="money += 1">资产+1</button><button @click="car.price += 1">汽车价格+1</button><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})// 用于更新money的方法function updateMoney(value:number){money.value += value}// 提供数据provide('moneyContext',{money,updateMoney})provide('car',car)
</script>
孙
注意:孙组件在接受的时候需要额外设置默认值,不然ts会警告
<template><div class="grand-child"><h3>我是孙组件</h3><h4>资产:{{ money }}</h4><h4>汽车:{{ car }}</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:(x:number)=>{}})let car = inject('car')
</script>