写文章不容易,点个赞呗兄弟
专注 Vue 源码分享,文章分为白话版和 源码版,白话版助于理解工作原理,源码版助于了解内部详情,让我们一起学习吧
研究基于 Vue 版本 【2.5.17】
如果你觉得排版难看,请点击 下面链接 或者 拉到 下面 关注公众号 也可以吧
【Vue 原理】Event – 源码版 之 自定义事件
Vue 的自定义事件很简单,就是使用 观察者模式 进行事件的监听和分发
Vue 封装的这个观察者模式,可以说是很完善了,这个可以独立抽取出来的在其他项目中使用的代码,只需要做一点点改动,把事件存储器换个地方(Vue 放在了实例上)
我经常在项目中使用,就是为了解耦或者解决一些异步的问题
今天来详细探索 Vue 的 自定义事件
首先,Vue 的事件存储器放在那里?
没错,放在 vm._events 中
你看,比如你这样监听事件
看到实例上保存了你的事件
1、事件存储器
vm._events
看下这个事件存储器在哪里生成的
首先,实例在初始化的时候,给实例增加一个事件存储器 _events
Vue.prototype._init = function(options) {initEvents(vm);
//... 初始化选项数据,解析模板,挂载 dom 等
}
function initEvents(vm) {vm._events = Object.create(null);
}
以后,所有这个实例监听的事件,就都存在这里了
那么,接下来就来看 自定义事件的源码了
下面的源码比较不太属于 Vue 的内容,比较独立,很实用,相信大家也都看得懂,这里主要起一个记录的作用
下面会有四个函数
绑定事件,$on
一次性绑定事件,$once
触发事件,$emit
解绑事件,$off
2、$on
注册事件,接收 事件名和回调,很清楚了,都能看得懂
Vue.prototype.$on = function(event, fn) {
var vm = this;
if (Array.isArray(event)) {for (var i = 0,l = event.length; i < l; i++) {this.$on(event[i], fn);
}
}
else {(vm._events[event] || (vm._events[event] = [])).push(fn);
}
// 为了链式调用
return vm
};
3、$once
单次注册。只监听一次,触发之后马上销毁
它妙就妙在,把回调包装了一下,在 回调执行时,先解绑事件,再调用原回调
Vue.prototype.$once = function(event, fn) {
var vm = this;
function on() {vm.$off(event, on);
fn.apply(vm, arguments);
}
on.fn = fn;
vm.$on(event, on);
// 为了链式调用
return vm
};
4、$emit
触发事件,接收事件名,然后拿到原本设置的回调,遍历调用
Vue.prototype.$emit = function(event) {
var vm = this;
var _events= event.toLowerCase();
var cbs = vm._events[_events];
if (cbs) {cbs = cbs.length > 1 ? toArray(cbs) : cbs;
var args = toArray(arguments, 1);
for (var i = 0, l = cbs.length; i < l; i++) {cbs[i].apply(vm, args);
}
}
// 为了链式调用
return vm
};
5、$off
取消监听事件或者移除监听回调
接收事件名 和 绑定时的事件回调
很简单的啦
Vue.prototype.$off = function(event, fn) {
var vm = this;
if (!arguments.length) {vm._events = Object.create(null);
return vm
}
// 递归调用
if (Array.isArray(event)) {for (var i = 0, l = event.length; i < l; i++) {this.$off(event[i], fn);
}
return vm
}
var cbs = vm._events[event];
if (!cbs) return vm
if (!fn) {vm._events[event] = null;
return vm
}
// 去掉特定的函数
if (fn) {
var cb;
var len = cbs.length;
// 遍历移除相应回调
while (len--) {cb = cbs[len];
if (cb === fn || cb.fn === fn) {cbs.splice(len, 1);
break
}
}
}
// 为了链式调用
return vm
};