什么是事件总线
事件总线是实现基于事件驱动模式的方式之一,事件发送者将事件消息发送到一个事件总线上,事件订阅者向事件总线订阅和接收事件,然后再处理接收到的事件。当然,订阅者不仅可以接收和消费事件,它们本身也可以创建事件,并将它们发送到事件总线上。
事件总线是对发布-订阅模式的一种实现。它是一种集中式事件处理机制,允许不同的组件之间进行彼此通信而又不需要相互依赖,达到一种解耦的目的。
实现
我们如果想要实现一个自定义的EventBus,那么首先就需要明白需要实现什么功能:
- on(eventKey, callback):新增事件监听器,可以一个eventKey绑定多个callback,使用emit来触发指定eventKey的所有callback
- once(eventKey, callback):监听一个自定义事件,但是只会执行一次,执行完毕后当前事件监听器会被移除
- off([eventKey, callback]):参数eventKey和callback都是可选的,
- 如果没有提供参数,则移除所有的事件监听器
- 如果只提供了eventKey,则移除该事件所有的监听器
- 如果同时提供了eventKey和callback,则只移除这个回调的监听器
- emit(eventKey, […args]):触发指定的事件,附加参数都会传给监听器回调。如果执行的是once定义的监听器,则执行后将会移除该监听器
具体实现
- on和once是用来注册函数的,并将其保存到数组中,因为要维持插入顺序和执行顺序一致
- emit根据key值找到存放回调函数的数组,并执行数组里面的所有函数,可以传入额外的参数
- off则根据传入的参数,也可能不传参数,找到函数并删除
class EventBus {
constructor() {
this.events = {}; // 初始值为空对象
}
//订阅事件
on(eventKey, fn, isOnce = false) {
const events = this.events; // 引用赋值
if (events[eventKey] == null) {
events[eventKey] = []; // 初始化eventKey对应的fn数组
}
// 将函数添加到数组中
events[eventKey].push({fn,isOnce});
}
//订阅事件,只会执行一次
once(eventKey, fn) {
// 代码复用
this.on(eventKey, fn, true);
}
//取消订阅
off(eventKey, fn) {
// 如果传入了函数,但是未指定eveneky,直接不执行
if (!eventKey && fn) return;
if (!eventKey && !fn) {
// 如果未传入参数,则清除所有绑定的函数
this.events = {};
} else if (eventKey && !fn) {
// 解绑当前eventKey对应的函数
this.events[eventKey] = [];
} else {
// 解绑eventKey和fn对应的函数
if (this.events[eventKey]) {
this.events[eventKey] = this.events[eventKey].filter(item => item.fn !== fn);
}
}
}
//发布事件
emit(eventKey, ...args) {
const fnList = this.events[eventKey]; // 引用赋值
if (fnList == null) return;
this.events[eventKey] = fnList.filter(item => {
const {fn,isOnce} = item;
fn(...args); // 执行函数,并传入额外参数
if (!isOnce) return true; // 如果不是once,表示后续还可以继续被执行
return false; // 如果是once,表示执行一次后就要被过滤掉
})
}
}