EventBus 事件总线
EventBus 是一个事件总线,常用于组件间通信。在 Vue.js 组件中,父子组件通信可以通过 props 和 $emit 实现,兄弟组件通信可以通过 provide 和 inject 实现,但是当组件层级较深或者关系复杂时,这些方式就显得不够灵活。EventBus 可以实现任意组件间通信,不受组件层级限制。
实现源码
ts
export class EventBus<T extends Record<string | symbol, any>> {
private listeners: Record<keyof T, ((...args: any[]) => void)[]> = {} as any;
$on<K extends keyof T>(event: K, callback: T[K]) {
if (!this.listeners[event]) {
this.listeners[event] = [];
}
this.listeners[event].push(callback);
}
$emit<K extends keyof T>(event: K, ...args: Parameters<T[K]>) {
const callbacks = this.listeners[event];
if (!callbacks || callbacks?.length === 0) {
return;
}
callbacks.forEach((callback) => {
callback(...args);
});
}
$off<K extends keyof T>(event: K, listener?: T[K]) {
if (!listener) {
delete this.listeners[event];
return;
}
const fns = this.listeners[event];
if (!fns || !fns.length) {
return;
}
const idx = fns.indexOf(listener);
if (idx !== -1) {
fns.splice(idx, 1);
}
}
clear() {
this.listeners = {} as any;
}
}介绍
本文 EventBus 是基于发布订阅模式实现的,因此可以在任何需要发布消息的地方使用。它包含四个方法:
$on(event: string, callback: Function):监听事件,当事件触发时执行回调函数。$emit(event: string, ...args: any[]):触发事件,执行所有监听该事件的回调函数。$off(event: string, listener?: Function):取消监听事件。clear():清空所有事件监听。
发布订阅模式是一种消息范式,消息的发送者(发布者)不会将消息直接发送给特定的接收者(订阅者),而是将消息分为不同的主题(频道),然后将消息发布到这些主题,订阅该主题的订阅者将收到消息。
基本用法
创建 EventBus 实例
ts
import { EventBus } from '@utils/event/eventBus';
export const eventBus = new EventBus<{
// 定义事件名称和参数类型
test: (msg: string) => void;
}>();发送事件
ts
import { eventBus } from './eventBus';
eventBus.$emit('test', 'Hello, EventBus!')监听事件
ts
import { eventBus } from './eventBus';
eventBus.$on('test', (data) => {
console.log(data); // Hello, EventBus!
})取消监听
ts
import { eventBus } from './eventBus';
eventBus.$off('test');组件通信示例
示例代码
vue
<script lang="ts" setup>
import { eventBus } from './eventBus';
import { ref } from 'vue';
const text = ref('')
eventBus.$on('test', (data) => {
text.value = data
});
</script>
<template>
<div class="message">接收到的信息:<strong>{{ text }}</strong></div>
</template>
<style lang="scss" scoped>
.message {
margin-bottom: 10px;
padding: 10px;
border: 1px solid #ccc;
}
</style>vue
<script lang="ts" setup>
import { eventBus } from './eventBus';
import BaseButton from '@/BaseButton/BaseButton.vue'
const onClick = () => {
const random = Math.floor(Math.random() * 10000);
eventBus.$emit('test', 'Hello, ' + random.toString());
};
</script>
<template>
<base-button @click="onClick">Emit</base-button>
</template>
<style lang="scss" scoped></style>