Vue 中事件相关的方法 vm.$on、vm.$off、vm.$once、vm.$emit,通过eventsMinxin方法挂在 Vue的原型上。
定义一个事件总线对象 events 管理所有事件:
每次执行$on的时候,都会在 events 中对应事件的回调函数列表中添加一项;
每次执行$emit的时候,会执行 events 中对应事件的所有函数。
/*
this.events = {
event1:[callback1,callback2,…………],
event2:[callback1,callback2,…………],...
}
*/
class EventBus {
constructor() {
// 事件总线对象
this.events = {};
}
// 监听 发布, event事件可以为数组和字符串,fn为事件触发时的回调函数
$on(event, fn) {
if (Array.isArray(event)) {
for (let i = 0; i < event.length; i++) {
// 监听数组中的每一个事件
this.$on(event[i], fn)
}
} else {
// 该事件的事件队列 如果存在就直接push进去fn,如果不存在就先创建为空数组
(this.events[event] || (this.events[event] = [])).push(fn);
}
}
// 触发事件event,...args为调用时的传参
$emit(event, ...args) {
const cbs = this.events[event];
cbs.forEach(fn => {
try {
fn(...args);
} catch (e) {
new Error('error')
}
});
}
// 清除某个event事件的fn
$off(event, fn) {
// 不传具体参数event时,就清空所有事件
if (!arguments.length) this.events = {};
if (Array.isArray(event)) {
for (let i = 0; i < event.length; i++) {
// 清除数组中的每一个事件
this.$off(event[i], fn)
}
return;
}
const cbs = this.events[event];
if (!cbs) return;
// 如果不传参数fn,就清除该事件的所有回调函数
if (arguments.length == 1) this.events[event] = null;
else {
// 该事件的事件队列
let n = cbs.length;
while (n--) {
if (cbs[n] === fn || cbs[n].fn === fn) {
cbs.splice(n, 1);
break;
}
}
}
}
// 注册监听事件和处理函数,触发一次后销毁, event为字符串
$once(event, fn) {
const self = this;
// 给fn做一层处理,使得触发时先清除
function handler() {
self.$off(event, handler);
fn.apply(self, arguments); //emit触发时会传入参数args
}
handler.fn = fn; //off清除时的判断依据
this.$on(event, handler);
}
}
const eb = new EventBus();
eb.$on('say', function (msg) {
console.log(msg);
});
eb.$emit('say', 'Hello world');