Vue2【Vue基础十】— 兄弟组件之间传值【中央事件总线,消息订阅与发布】
Vue2 : 【Vue基础九】–父子组件传值
一、 兄弟组件
1-1 事件总线
- 使用Vue3,会发现,原本得心应手的eventBus突然不灵了
- Vue3不再提供$on与emit函数,Vue实例不再实现事件接口。官方推荐引入外部工具实现,或者自己手撸一个事件类
1-1-1 使用EventBus
- 引入/编写事件库
- 在入口文件中挂载
- 在组件中引入并使用
1-1-2 不借助插件的原生使用方式
-
引入/编写事件库
- 方法一: 引入官方推荐的mitt
- 方法二: 手撸一个简单的发布/订阅类
// eventBus.js export default class EventBus{constructor(){this.events = {};}emit(eventName, data) {if (this.events[eventName]) {this.events[eventName].forEach(function(fn) {fn(data);});}}on(eventName, fn) {this.events[eventName] = this.events[eventName] || [];this.events[eventName].push(fn);}off(eventName, fn) {if (this.events[eventName]) {for (var i = 0; i < this.events[eventName].length; i++) {if (this.events[eventName][i] === fn) {this.events[eventName].splice(i, 1);break;}};}} }
-
在入口文件main.js中执行挂载
// main.js
import { createApp } from 'vue'
import App from './App.vue'
// ① 引入事件类
// 自己编写的或者mitt皆可
import EventBus from 'lib/bus.js'
// 或者:import EventBus from 'mitt'
const $bus = new EventBus()// ② 挂载
// 1.使用provide提供
app.provide('$bus', $bus)
// 2.挂载到this上
app.config.globalProperties.$bus = $bus
- 在组件中引入并使用
- 在creates中使用
// Button.vue export default {created() {this.$bus.emit('ButtonCreated')} }
- 在setup中使用
注意: 因为在setup中无法访问到应用实例(this),如果你需要在setup中使用eventBus,则需要通过provide/inject方式引入
// Button.vue import { inject } from 'vue' export default {setup() {const $bus = inject('$bus')$bus.emit('ButtonSetup')} }
二、 爷孙组件
2-1 props和$emit
最常用的父子组件通信方式,爷爷–> 父 ----> 子
1、 父组件向子组件传递数据是通过prop传递的,子组件传递数据给父组件是通过$emit触发事件做到的
2、 处理父子组件之间的数据传输有一个问题:
- 多层嵌套,父组件A下面有子组件B,组件B下面有组件C,A想传递给C怎么办?
- 采用第一种方法,必须让组件A通过prop传递消息给组件B,组件B再传给C
- Vue2.4之后,引入了attrs和listeners来解决这个问题
2-2 attrs和listeners
1、 attrs和listeners的过程:
父组件A下面有子组件B,组件B下面有组件C,组件A传递数据给组件B
- C组件
Vue.component('C', {template: `<div> <input type="text" v-model="$attrs.messageC" @input="passCData($attrs.messageC)"> </div>`,methods: {passCData(val) {// 触发父组件A中的事件this.$emit('getCData',val)}}
})
- B组件
Vue.component('B', {data() {return {myMessage: this.message}},template: `<div> <input type="text" v-model="myMessage" @input="passData(myMessage)"> <C v-bind="$attrs" v-on="$listeners"></C> </div> `// 得到父组件传递过来的数据props: ['message'],methods: {passData(val) {// 触发父组件中的事件this.$emit('getChildData',val)}}
})
- A组件
Vue.component('A',{template: `<div> <p>this is parent compoent!</p> <B :messageC="messageC" :message="message" v-on:getCData="getCData" v-on:getChildData="getChildData(message)"> </B> </div> `,data() {return {message: 'Hello',messageC: 'Hello c'}},methods: {getChildData(val) {console.log('这是来自B组件的数据')},// 执行C子组件触发的事件getCData(val) {console.log("这是来自C组建的数据:"+val)}}
})
var app = new Vue({el: '#app',template: `<div><A></A></div>`
})
2、 解析:
- C组件中能直接触发getData的原因: B组件调用C组件时,使用v-on绑定了$listeners属性
- 通过v-bind绑定$attrs属性,C组件可以直接获取到A组件中传递下来的props(除了B组件中props声明的)
三、 v-model
1、 父组件通过v-model传递值给子组件时,会自动传递一个value的prop属性,在子组件中通过this.$emit(‘input’,val)自动修改v-model绑定的值
后续回来得看看
- https://blog.csdn.net/m0_56986233/article/details/121405388
- https://www.zhihu.com/question/466846675