Event Bus实现兄弟组件通信
Event Bus(事件总线)是一种组件间通信的模式,主要用于非父子关系的组件之间的通信。它通过创建一个全局的 Vue 实例作为事件中心,任何组件都可以通过这个中心来触发事件或监听事件,从而实现跨组件的数据传递和状态同步。
实现原理:
-
创建全局事件总线: 创建一个单独的 Vue 实例作为事件总线,用于在不同组件之间传递消息。
-
发送事件: 在一个组件中触发事件,通过
EventBus.$emit
方法发送事件和数据。 -
接收事件: 在另一个组件中监听事件,通过
EventBus.$on
或EventBus.$once
方法接收事件和数据。 -
清理事件监听器: 组件销毁时,移除事件监听器,避免内存泄漏。
优点
-
解耦: 组件之间不需要直接引用彼此,提高了组件的独立性和可复用性。
-
灵活性: 可以方便地扩展事件和数据传递,适用于复杂的多组件通信场景。
-
易于维护: 事件总线模式使得组件之间的通信更加清晰,便于维护和调试。
缺点
-
命名冲突: 如果多个组件使用相同的事件名称,可能会导致命名冲突。
-
调试困难: 由于事件总线中的事件传递路径不明确,调试时可能较难追踪问题所在。
-
性能开销: 频繁触发事件可能导致性能开销增加,尤其是在大型应用中。
-
状态管理复杂: 对于更复杂的状态管理,使用 Vuex 可能更为合适,特别是当组件间的通信变得更加复杂时。
代码示例
vue2.x 使用
创建全局事件总线:
// main.jsimport 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.jsexport 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 进行状态管理,以更好地组织和管理全局状态