EventBus是事件总线的意思。所谓事件总线模式,其实就和发布订阅模式非常类似,比如我们订阅了一个公众号,公众号发布文章之后我们就能收到信息,这就是一种订阅发布的关系。
再比如在Vue2项目中,我们可以使用$on、$emit来实现事件的监听和触发,这其实就是一种事件总线的思想在里面,只不过Vue帮我们实现好了。
在我们的JavaScript中,可以给元素添加一个点击监听事件,当用户点击的时候,点击事件怎会被执行,这也是一种事件总线的思想在里面,就好比元素订阅了点击事件,用户发布或出触发点击事件。
EventBus实现思路:
使用$on订阅事件
使用$emit发布事件
$emit发布事件可以传参
实现$off取消订阅
实现$once执行一次
<script>
class EventBus {
constructor() {
this.eventObj = {}; // 用于存储所有订阅事件
this.callbcakId = 0; // 每个函数的ID
}
// 订阅事件,类似监听事件$on('key',()=>{})
$on(name, callbcak) {
// 判断是否存储过
if (!this.eventObj[name]) {
this.eventObj[name] = {};
}
// 定义当前回调函数id
const id = this.callbcakId++;
this.eventObj[name][id] = callbcak; // 以键值对的形式存储回调函数
return id; // 将id返回出去,可以利用该id取消订阅
}
// 发布事件,类似于触发事件$emit('key')
$emit(name, ...args) {
// 获取存储的事件回调函数数组
const eventList = this.eventObj[name];
// 执行所有回调函数且传入参数
for (const id in eventList) {
eventList[id](...args);
// 如果是订阅一次,则删除
if(id.indexOf('D') !== -1) {
delete eventList[id];
}
}
}
// 取消订阅函数,类似于$off('key1', id)
$off(name, id) {
console.log(this.eventObj)
// 删除存储在事件列表中的该事件
delete this.eventObj[name][id];
console.info(`${id}id事件已被取消订阅`)
// 如果这是最后一个订阅者,则删除整个对象
if (!Object.keys(this.eventObj).length) {
delete this.eventObj[name];
}
}
// 订阅事件,只会执行一次,为了方便,id上直接加上一个标识d
$once(name, callbcak){
// 判断是否存储过
if (!this.eventObj[name]) {
this.eventObj[name] = {};
}
// 定义当前回调函数id,添加D则代表只执行一次
const id = "D" + this.callbcakId++;
this.eventObj[name][id] = callbcak; // 以键值对的形式存储回调函数
return id; // 将id返回出去,可以利用该id取消订阅
}
}
// 初始化EventBus
let bus = new EventBus();
// 订阅事件
bus.$on('key1', (name, age) => {
console.info("我是订阅事件A:", name, age);
})
bus.$once("key1", (name, age) => {
console.info("我是订阅事件B:", name, age);
})
bus.$on("key2", (name) => {
console.info("我是订阅事件C:", name);
})
// 发布事件key1
bus.$emit('key1', "小猪课堂", 26);
console.info("再触发一次key1")
bus.$emit('key1', "小猪课堂", 26);
// 发布事件
bus.$emit('key2', "小猪课堂");
</script>
// 打印结果:
// 我是订阅事件A: 小猪课堂 26
// 我是订阅事件B: 小猪课堂 26
// 再触发一次key1
// 我是订阅事件A: 小猪课堂 26
// 我是订阅事件C: 小猪课堂