手写一个EventBus事件处理中心(解读vue的事件方法)
vue中的事件相关的方法
扒一扒Vue的源码,vue中事件相关的方法,无非这几个,vm.$on, vm.$off, vm.$once, vm.$emit。通过eventsMinxin方法挂在Vue的原型上,本文就是通过解读vue的源码,实现一个简单的事件处理中心类 EventBus。
首先,我们来回顾下这几个方法的用法:
vm.$on( event, callback )
参数:
- {string | Array<string>} event (数组只在 2.2.0+ 中支持)
- {Function} callback
vm.$once( event, callback )
参数:
- {string} event
- {Function} callback
vm.$off( [event, callback] )
参数:
- {string | Array<string>} event (只在 2.2.2+ 支持数组)
- {Function} [callback]
vm.$emit( eventName, […args] )
参数:
- {string} eventName
- [...args]
触发当前实例上的事件。附加参数都会传给监听器回调。
实现一个EventBus
首先,我们先定义一个全局的类EventBus。
function EventCenter() {
// 全局定义一个_events属性,存储事件
this._events = Object.create(null);
}
// 通过eventMixin方法在EventCenter的原型上挂载方法
eventMixin(EventCenter);
export default EventCenter;接下来,我们来实现eventMixin方法。
export default function eventMixin(EventCenter) {
EventCenter.prototype.on = function(event, fn) {...}
EventCenter.prototype.off = function(event, fn) {...}
EventCenter.prototype.once = function(event, fn) {...}
EventCenter.prototype.once = function(event) {...}
}ec.on( event, fn ), event值可以为数组和字符串,fn为事件触发时的回调函数
EventCenter.prototyoe.on = function(event, fn) {
const ec = this;
if (Array.isArray(event)) {
for (let i = 0; i < event.length; i++) {
ec.on(event[i], fn)
}
} else {
(ec._events[event] || (ec._events[event] = [])).push(fn);
}
}ec.off( event, fn ), event值可以为数组和字符串,fn为事件触发时的回调函数
EventCenter.prototyoe.on = function(event, fn) {
const ec = this;
// 判断如果不传参数, 则移除所有事件
if (!arguments.length) {
ec._events = Object.create(null);
}
// event为数组时,遍历移除事件
if (Array.isArray(event)) {
for(let i = 0; i < event.length; i++) {
ec.off(event[i], fn);
}
return ec;
}
const cbs = ec._events[event];
// 回调不存在 直接返回
if (!cbs) {
return ec;
}
// cbs为一个或者fn不存在,ec._events[event] = null, 直接移除
if (arguments.length === 1) {
ec._events[event] = null;
return ec;
}
if (!fn) {
ec._events[event] = null;
return ec;
}
// 否则,遍历cbs,移除cbs中为fn的回调函数
let cb;
let i = cbs.length;
// 从后向前遍历,移除当前监听器时,不会影响未遍历过的监听器的位置。
while (i--) {
cb = cbs[i];
if (cb === fn || cb.fn === fn) {
cbs.splice(i, 1);
break;
}
}
return ec;
}ec.once( event, fn ), event值可以为字符串,fn为事件触发时的回调函数
const ec = this;
// 自定义一个_on方法,先解绑_on, 然后通过调用apply方法执行fn。
function _on() {
ec.off(event, _on);
fn.apply(ec, arguments);
}
// 这句暂时没看懂,等之后查了资料补充
_on.fn = fn;
ec.on(event, _on);
return ec;ec.emit( event, ...args ), event值可以为字符串,...args为调用时的传参
EventCenter.prototype.emit = function(event) {
const ec = this;
let cbs = ec._events[event];
if (cbs) {
// 拿到传参
const args = Array.from(arguments).slice(1);
for(let i = 0; i < cbs.length; i++) {
try {
cbs[i].apply(ec, args);
} catch(e) {
new Error('error');
}
}
}
return ec;
}至此,一个简单的事件中心就完成了。
参考: vue源码 core/instance/events.js