前言
大家好,我是 CoderBin,本次又来到了 Vue 的专题-组件通信。毫无疑问,组件通信是 Vue 中非常重要的技术之一,它的出现能够使我们非常方便的在不同组件之间进行数据的传递,以达到数据交互的效果。所以,学习组件通信技术是非常有必要的,本文将总结 Vue 中关于组件通信的八种方式,帮助大家在使用 Vue 的过程中更加得心应手!
如果文中有不对、疑惑的地方,欢迎在评论区留言指正🌻
Vue 专题系列
【1】Vue常用修饰符大全
【2】Vue内置指令大全
【3】点击前往Vue专栏查看更多
高频面试总结
【1】关于Vue的一些高频面试题总结
【2】75道关于CSS的高频面试题总结,请注意查收!🔥
一、什么是组件通信
在开始之前我们需要明白什么是组件通信,组件通信可以拆分为两个部分:
都知道组件是vue最强大的功能之一,vue中每一个.vue文件我们都可以视之为一个组件,简单来说组件就是对 UI 结构的复用。
通信指的是发送者通过某种媒体以某种格式来传递信息到收信者以达到某个目的。广义上,任何信息的交通都是通信。而组件间通信即指组件(.vue)通过某种方式来传递信息以达到某个目的,举个栗子我们在使用UI框架中的table组件,可能会往table组件中传入某些数据,这个本质就形成了组件之间的通信
二、为什么要进行组件通信
通信的本质是信息同步,共享。回到 vue 中,每个组件之间的都有独自的作用域,组件间的数据是无法共享的但实际开发工作中我们常常需要让组件之间共享数据,这也是组件通信的目的要让它们互相之间能进行通讯,这样才能实现数据间的交互,完成某种功能的开发。
三、组件通信的分类
组件间通信的分类可以分成以下
父子组件之间的通信
兄弟组件之间的通信
祖孙与后代组件之间的通信
非关系组件间之间的通信
他们之间的关系如下图:
四、组件间通信的八中方案
整理vue中 8 种常规的通信方案
通过 props 传递
通过 emit 触发自定义事件
使用 ref
事件总线 EventBus
attrs
依赖注入:Provide 与 Inject
双向绑定:v-model
全局状态管理:Vuex
1. defineProps 父传子
使用场景:父组件传递给子组件
Children.vue,子组件定义 props,接收父组件传递过来的数据
const props = defineProps:{ // 字符串形式 name: String // 接收的类型参数 // 对象形式 age:{ type:Number, // 接收的类型为数值 defaule:18, // 默认值为18 require:true // age属性必须传递 } }
复制代码
Father.vue,父组件以属性值的方式向子组件传递数据
<Children name="jack" :age=18 />
复制代码
2. defineEmits 子传父
适用场景:子组件传递数据给父组件
Children.vue 中
// 定义自定义事件const emit = defineEmits(['emit1'])
// 使用事件const handleClick = () =>{ emit('emit1', {name: 'CoderBin'})}
复制代码
Father.vue
<Children @emit1="handleEmit1"/>
const handleEmit1 = (data) => { console.log(data.name) // CoderBin}
复制代码
3. ref/defineExpose 使用
父组件在使用子组件的时候设置ref
父组件通过设置子组件ref来获取数据
Children组件中,将需要被父组件使用的变量或方法暴露出去
const name = ref("CoderBin")
const handleClick = () => { console.log("CoderBin)}
defineExpose({name, handleClick})
复制代码
father.vue中
<Children ref="childrenRef"/>
// 定义 refconst childrenRef = ref(null)
console.log(children.value.name) // 可以使用子组件中暴露的变量和方法
复制代码
4. 事件总线 EventBus
使用场景:兄弟组件传值
我们创建一个 Bus.js 文件,用来控制数据和注册事件的。
Bus.js 里有一个 Bus 类
eventList 是必须项,用来存放事件列表的。
constructor 里除了 eventList 外,其他都是自定义数据,公共数据就是存在这里的。
$on 方法用来注册事件。
$emit 方法可以调用 $on 里的事件。
$off 方法可以注销 eventList 里的事件。
然后需要用到总线的组件,都导入 Bus.js ,就可以共同操作一份数据了。
Bus.js
import { ref } from 'vue'
class Bus { constructor() { // 收集订阅信息,调度中心 this.eventList = {}, // 事件列表,这项是必须的 // 下面的都是自定义值 this.msg = ref('这是一条总线的信息') }
// 订阅 $on(name, fn) { this.eventList[name] = this.eventList[name] || [] this.eventList[name].push(fn) }
// 发布 $emit(name, data) { if (this.eventList[name]) { this.eventList[name].forEach((fn) => { fn(data) }); } }
// 取消订阅 $off(name) { if (this.eventList[name]) { delete this.eventList[name] } }}
export default new Bus()
复制代码
父组件
// Parent.vue
<template> <div> 父组件: <span style="margin-right: 30px;">message: {{ message }}</span> <span>msg: {{ msg }}</span> </div> <Child></Child></template>
<script setup>import { ref } from 'vue'import Bus from './Bus.js'import Child from './components/Child.vue'
const msg = ref(Bus.msg)
const message = ref('hello')
// 用监听的写法Bus.$on('changeMsg', data => { message.value = data})
</script>
复制代码
子组件
// Child.vue
<template> <div> 子组件: <button @click="handleBusEmit">触发Bus.$emit</button> <button @click="changeBusMsg">修改总线里的 msg</button> </div></template>
<script setup>import Bus from '../Bus.js'
function handleBusEmit() { Bus.$emit('changeMsg', '雷猴啊')}
function changeBusMsg() { // console.log(Bus.msg) Bus.msg.value = '在子组件里修改了总线的值'}</script>
复制代码
5 attrs 的使用
attrs:包含父作用域除 class 和 style 除外的非 props 属性集合
父组件
<template> <child :msg1="msg1" :msg2="msg2" title="3333"></child></template>
<script setup> import child from './child.vue' import { ref,reactive } from 'vue const msg1 = ref('111') const msg2 = ref('222')</script>
复制代码
子组件
<script setup> import { defineProps,useContext,useAttars } from 'vue'
const props = defineProps({ msg1: String })
// 方法1
const ctx = useContext()console.log(ctx.attars) // {msg2:'222',title:'333'}
// 方法2 const attrs = useAttars()console.log(attars) // {msg2:'2222',title:'3333'}</script>
复制代码
6 provide/inject 依赖注入
适用场景:父(祖)组件给后代组件传值,如果传的是函数,后代组件还能通过参数的形式将数据传给父组件
父(祖)组件
<script setup>import { ref, provide } from 'vue'
const name = ref('CoderBin')
const handleClick = (data) => { // 接收子组件传递过来的参数 console.log(data)}
provide('pname', name.value) // 传递变量provide('phandleClick', handleClick) // 传递方法</script>
复制代码
后代组件
<script setup>import { inject } from "vue"
// 接收数据const name = inject('pname')
const handleClick = inject('phandleClick')// 调用父组件传递过来的函数,并传了个参数回去handleClick('test')</script>
复制代码
7. v-model
v-model 是 Vue 的一个语法糖。在 Vue3 中的玩法就更多(晕)了。
官方文档 v-model
父组件
// Parent.vue
<template> <Child v-model="message" /></template>
<script setup>import { ref } from 'vue'import Child from './components/Child.vue'
const message = ref('test')</script>
复制代码
子组件
// Child.vue
<template> <div @click="handleClick">{{modelValue}}</div></template>
<script setup>import { ref } from 'vue'
// 接收const props = defineProps([ // 接收父组件使用 v-model 传进来的值,必须用 modelValue 这个名字来接收 'modelValue'])
// 必须用 update:modelValue 这个名字来通知父组件修改值const emit = defineEmits(['update:modelValue'])
function handleClick() { // 参数1:通知父组件修改值的方法名 // 参数2:要修改的值 emit('update:modelValue', 'CoderBin')}
</script>
复制代码
8. 全局状态管理:vuex
适用场景: 复杂关系的组件数据传递,Vuex 作用相当于一个用来存储共享变量的容器,详细使用可前往
【1】Vuex: 全面拥抱Vue3,Vuex4 最新详解教程!
【2】Pinia(vuex5): 新一代的状态管理器?关于 Pinia 的全方位解析!
简单介绍:
5. 小结
父子关系的组件数据传递选择 props 与 $emit进行传递,也可选择ref
兄弟关系的组件数据传递可选择$bus,其次可以选择进行传递
祖先与后代组件数据传递可选择Provide与 Inject
复杂关系的组件数据传递可以通过vuex存放共享的变量
往期推荐 💐 🌸 🌹 🌻 🌺 🍁
高阅读好文:
【1】关于Vue的一些高频面试题总结
【2】75道关于CSS的高频面试题总结,请注意查收!🔥
【3】Vue内置指令大全
【4】Vue常用修饰符大全
【4】面试官:你说说 js 中实现继承有哪几种方法?
----- 🌻 查看全部 🍁 -----
每文一句:时间,就像海绵里的水,只要愿挤,总是有的。——鲁迅
本次的分享就到这里,如果本章内容对你有所帮助的话欢迎点赞+收藏。文章有不对的地方欢迎指出,有任何疑问都可以在评论区留言。希望大家都能够有所收获,大家一起探讨、进步!
评论