手写一个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