Event Bus实现兄弟组件通信
Event Bus(事件总线)是一种组件间通信的模式,主要用于非父子关系的组件之间的通信。它通过创建一个全局的 Vue 实例作为事件中心,任何组件都可以通过这个中心来触发事件或监听事件,从而实现跨组件的数据传递和状态同步。
实现原理:
-
创建全局事件总线: 创建一个单独的 Vue 实例作为事件总线,用于在不同组件之间传递消息。
-
发送事件: 在一个组件中触发事件,通过
EventBus.$emit
方法发送事件和数据。 -
接收事件: 在另一个组件中监听事件,通过
EventBus.$on
或EventBus.$once
方法接收事件和数据。 -
清理事件监听器: 组件销毁时,移除事件监听器,避免内存泄漏。
优点
-
解耦: 组件之间不需要直接引用彼此,提高了组件的独立性和可复用性。
-
灵活性: 可以方便地扩展事件和数据传递,适用于复杂的多组件通信场景。
-
易于维护: 事件总线模式使得组件之间的通信更加清晰,便于维护和调试。
缺点
-
命名冲突: 如果多个组件使用相同的事件名称,可能会导致命名冲突。
-
调试困难: 由于事件总线中的事件传递路径不明确,调试时可能较难追踪问题所在。
-
性能开销: 频繁触发事件可能导致性能开销增加,尤其是在大型应用中。
-
状态管理复杂: 对于更复杂的状态管理,使用 Vuex 可能更为合适,特别是当组件间的通信变得更加复杂时。
代码示例
vue2.x 使用
创建全局事件总线:
// main.js
import Vue from "vue";
import App from "./App";
import store from "./store";
import router from "./router";
new Vue({
el: "#app",
router,
store,
beforeCreate() {
Vue.prototype.$bus = this;
},
render: (h) => h(App),
});
parent.vue (父组件)
// parent.vue
<template>
<div class="parent">
<h2>父组件</h2>
<div class="box">
<first-son />
<second-son />
</div>
</div>
</template>
<script>
import firstSon from './firstSon.vue';
import secondSon from './secondSon.vue';
export default {
name: 'parent',
data() {
return {
}
},
components: { firstSon, secondSon },
methods: {
}
}
</script>
firstSon.vue (子组件1)
// firstSon.vue
<template>
<div class="son">
<h2>子组件1</h2>
<el-button type="primary" @click="sendMessage">发送消息</el-button>
</div>
</template>
<script>
export default {
name: 'firstSon',
data() {
return {
msg: 'Hello from brother component!'
}
},
methods: {
sendMessage(){
this.$bus.$emit('sendBrotherMsg', this.msg);
}
}
}
</script>
secondSon.vue (子组件2)
// secondSon.vue
<template>
<div class="grandpa">
<h2>子组件2</h2>
<div>{{ msg }}</div>
</div>
</template>
<script>
export default {
name: 'secondSon',
data() {
return {
msg: '',
}
},
created() {
this.getSendMessage();
},
beforeDestroy() {
// 清理事件监听器
this.$bus.$off('sendBrotherMsg')
},
methods: {
getSendMessage() {
// 添加事件监听器接受兄弟组件传过来的数据
this.$bus.$on('sendBrotherMsg', msg => {
console.log('msg::: ', msg);
this.msg = msg;
})
}
}
}
</script>
上述示例中:
-
parent.vue 组件为父组件,导入 firstSon.vue 与 secondSon.vue 组件。
-
firstSon.vue 组件点击按钮触发事件
this.$bus.$emit()
方法发送sendBrotherMsg
事件和this.msg
参数。 -
secondSon.vue 组件通过
this.$bus.on()
接收sendBrotherMsg
事件和msg
参数,并在cerated
生命周期执行调用。
vue3.x使用
在 Vue 3.x 中,虽然不再推荐使用全局的 Vue 实例作为事件总线(因为 Vue 3 已经移除了全局 Vue 构造函数),但我们仍然可以使用类似的模式来实现兄弟组件间的通信。通常我们会创建一个普通的 JavaScript 模块来充当事件总线的角色。下面是如何在 Vue 3 中实现这一点的示例。
创建事件总线模块
首先,我们需要创建一个事件总线模块,它可以是一个简单的 JavaScript
对象,用来存储事件监听器,并提供 on
和 emit
方法来监听和触发事件。
// eventBus.js
export const EventBus = {
listeners: {} as Record<string, Function[]>,
$on(event: string, callback: (...args: any[]) => void): void {
if (!this.listeners[event]) {
this.listeners[event] = [];
}
this.listeners[event].push(callback);
},
$emit(event: string, ...args: any[]): void {
const callbacks = this.listeners[event];
if (callbacks) {
callbacks.forEach(callback => callback(...args));
}
},
$off(event: string, callback: (...args: any[]) => void): void {
const callbacks = this.listeners[event];
if (callbacks) {
this.listeners[event] = callbacks.filter(cb => cb !== callback);
}
}
};
parent.vue (父组件)
// parent.vue
<template>
<div class="parent">
<h2>父组件</h2>
<div class="box">
<firstSon />
<secondSon />
</div>
</div>
</template>
<script setup lang="ts" name="parent">
import firstSon from './firstSon.vue';
import secondSon from './secondSon.vue';
</script>
firstSon.vue (子组件1)
// firstSon.vue
<template>
<div class="firstSon">
<h2>子组件1</h2>
<el-button type="primary" @click="sendMessage">发送消息</el-button>
</div>
</template>
<script setup lang="ts" name="firstSon">
import { EventBus } from './eventBus.ts';
function sendMessage() {
const data = { key: 'value' };
EventBus.$emit('sendBrotherMsg', data);
}
</script>
secondSon.vue (子组件2)
// secondSon.vue
<template>
<div class="secondSon">
<h2>子组件2</h2>
<div>{{ receivedMessage }}</div>
</div>
</template>
<script setup lang="ts" name="secondSon">
import { ref, onMounted, onBeforeUnmount } from 'vue';
import { EventBus } from './eventBus';
const receivedMessage = ref('');
onMounted(() => {
// 监听事件
EventBus.$on('sendBrotherMsg', handleData);
});
onBeforeUnmount(() => {
EventBus.$off('sendBrotherMsg', handleData);
});
// 接受参数
const handleData = (data: { key: string }) => {
console.log('data::: ', data);
receivedMessage.value = data.key;
};
</script>
通过上述示例,我们可以看到:
- 创建事件总线:使用一个简单的
TypeScript
对象作为事件总线。 - 触发事件:在组件 firstSon.vue 中通过
EventBus.$emit
方法触发事件,并传递数据。 - 监听事件:在组件 secondSon.vue 中通过
onMounted
生命周期钩子添加事件监听器,并通过handleData
函数处理接收到的数据。 - 清理事件监听器:在组件销毁之前,通过
onBeforeUnmount
生命周期钩子移除事件监听器,防止内存泄漏。
总结
Event Bus 是 Vue 中一种常用的组件间通信模式,它利用全局的 Vue 实例作为事件中心,允许组件之间通过触发和监听事件来进行通信。这种模式简单易用,适用于简单的跨组件通信场景。然而,在更复杂的项目中,可能需要考虑使用 Vuex 进行状态管理,以更好地组织和管理全局状态