目录
一、概念
事件总线也是一种通信方式,只不过它的功能比较强大,不在局限于父传子或兄弟组件之间通信,它可以跨组件通信,通过事件总线传递的值,不管哪个组件都可以获取到
总而言之,可以在各个模块中通信并且传递数据
事件总线属于一种观察者模式,其中包括三个角色 :
- 发布者(Publisher):发出事件(Event)
- 订阅者(Subscriber):订阅事件(Event),并且会进行响应(Handler)
- 事件总线(EventBus):无论是发布者还是订阅者都是通过事件总线作为中台的
二、手写
1. 代码
class StarEventBus {
constructor() {
// 事件名称 和 方法的映射关系
this.eventMap = {};
}
/**
* 监听
* 因为可能有相同的名字监听,但是回调函数不一样
* 所以使用数组存储
*/
on(eventName, fn) {
if (!this.eventMap[eventName]) {
this.eventMap[eventName] = [];
}
this.eventMap[eventName].push(fn);
}
/**
* 发射事件
* 一发射就自动调用方法
*/
emit(eventName, ...args) {
if (!this.eventMap[eventName]) return;
this.eventMap[eventName].forEach((fn) => {
fn(...args);
});
}
/**
* 删除事件
* 删除事件名和其对应的函数
*/
off(eventName, fn) {
if (!this.eventMap[eventName]) return;
// 可能拥有相同的事件名和相同的函数,所以循环删除
this.eventMap[eventName].forEach((itemFn, index) => {
if (itemFn === fn) {
// 使其位置还在,不过是为空
delete this.eventMap[eventName][index];
}
});
// 过滤空的值
this.eventMap[eventName] = this.eventMap[eventName].filter((item) => item);
// 如果eventFns已经清空了
if (this.eventMap[eventName].length === 0) {
delete this.eventMap[eventName];
}
}
/**
* 清空事件名对应的事件函数数组
*/
clear(eventName) {
if (!this.eventMap[eventName]) return;
delete this.eventMap[eventName];
}
/**
* 清空所有事件
*/
clearAll() {
this.eventMap = {};
}
}
2. 栗子 🌰
// 1. 创建对象
const eventBus = new StarEventBus();
// 2. 监听方法
eventBus.on('btnClick', () => {
console.log('俺被惦记了');
});
const btnClick = () => {
console.log('俺也被惦记了');
};
eventBus.on('btnClick', btnClick);
setTimeout(() => {
// 4. 删除方法
eventBus.off('btnClick', btnClick);
}, 3000);
const btnDom = document.querySelector('button');
btnDom.onclick = function () {
console.log('btn 点击');
// 3. 发射方法
eventBus.emit('btnClick');
};
三、Vue2 中
Vue2默认是带有事件总线的功能,因为vue实例自带了以下方法
1. 在 main.js 中注册
// 在入口 main.js 中,创建一个 bus 总线,这样全局都可以使用
Vue.prototype.$bus = new Vue()
2. 在某组件发射事件
<template>
<button @click="btnClick">发射</button>
</template>
<script>
export default {
methods: {
btnClick() {
this.$bus.$emit("emitFn", '我来啦!!!');
}
}
};
3. 在某组件监听事件
<template>
<div>test</div>
</template>
<script>
export default {
mounted() {
// 监听 emitFn 事件
this.$bus.$on("emitFn", (res) => {
console.log(res) // 我来啦!!!
});
// 只会促发一次的监听
this.$bus.$once("gulugulu", (res) => {
console.log('once') // once
});
}
};
4. 移除事件
<template>
<div>test</div>
</template>
<script>
export default {
mounted() {
// 监听 emitFn 事件
this.$bus.$on("emitFn", this.sayHello);
},
methods: {
sayHello(res) {
console.log(res) // 我来啦!!!
}
},
// 移除事件
beforeDestroy() {
this.$bus.$off("emitFn")
// this.$bus.$off("emitFn", this.sayHello)
}
};
四、Vue3 中
Vue3中推荐一些第三方库,比如mitt
1. 安装
npm install mitt
2. 封装一下
新建eventbus.js
// 导入
import mitt from 'mitt';
// 创建
const emitter = mitt();
// 导出
export default emitter;
3. 发射事件
<template>
<div class="son-layout">
<button @click="emitSon">son</button>
</div>
</template>
<script>
// 导入封装的
import emitter from '../assets/eventBus';
export default {
name: 'son',
methods: {
emitSon() {
console.log('我发射啦');
// 使用
emitter.emit('sonEmit', { name: '我是son' });
}
}
};
</script>
<style></style>
4. 监听事件
<template>
<div class="zhong-layout">zhong</div>
</template>
<script>
// 导入
import emitter from '../assets/eventBus';
export default {
name: 'zhong',
mounted() {
// 监听sonEmit事件
emitter.on('sonEmit', (value) => {
console.log(value.name, '收到啦');
});
// 监听所有事件 type : 事件的名字 info:对应的数据
// 如果有两个事件,会执行两次,不会有冲突哒~
emitter.on('*', (type, info) => {
console.log(info);
console.log('* listener', type, info);
});
},
};
</script>
<style>
</style>
5. 取消事件
// 取消所有的事件监听
emitter.all.clear()
// 取消指定事件监听,方法要使用同一个 如 : sonEmit.
function onFoo(){}
emitter.on('sonEmit', onFoo)
emitter.off('sonEmit', onFoo)