在Vue.prototype._init 中有一些init函数,今天我们来研究这些init函数
Vue.prototype._init = function (options) {
......
{
initProxy(vm);
}
......
initLifecycle(vm);
initEvents(vm);
initRender(vm);
callHook$1(vm, 'beforeCreate', undefined, false /* setContext */);
initInjections(vm); // resolve injections before data/props
initState(vm);
initProvide(vm); // resolve provide after data/props
callHook$1(vm, 'created');
......
}
上一篇中已经研究了initLifecycle,今天我们往下研究
Vue.prototype._init = function (options) {
......
initEvents(vm);
......
}
看下源码 initEvents(vm);
function initEvents(vm) {
vm._events = Object.create(null);
vm._hasHookEvent = false;
// init parent attached events
const listeners = vm.$options._parentListeners;
if (listeners) {
updateComponentListeners(vm, listeners);
}
}
vm._events = Object.create(null);
该行代码创建了一个原型为null的空对象,并将这个对象赋值给vm实例的_events属性
vm._hasHookEvent = false;
该行代码 给vm实例的_hasHookEvent属性赋值为false
const listeners = vm.$options._parentListeners;
从英文注释,我们可以知道这行代码是为了初始化父组件添加的事件
因为是取的组件$options属性的_parentListeners,这个比较复杂,涉及到双向绑定和虚拟dom,我们后续研究,先暂时理解为父组件绑定在当前组件上的事件
如果存在事件listeners
则执行下面这段代码
if (listeners) {
updateComponentListeners(vm, listeners)
}
function updateComponentListeners(vm, listeners, oldListeners) {
target$1 = vm;
updateListeners(listeners, oldListeners || {}, add$1, remove$1, createOnceHandler$1, vm);
target$1 = undefined;
}
target$1 = vm;
这行代码的主要作用是保留对vm实例的引用
updateListeners(listeners, oldListeners || {}, add, remove, vm)
这行代码执行updateListeners方法
function add$1(event, fn) {
target$1.$on(event, fn);
}
function remove$1(event, fn) {
target$1.$off(event, fn);
}
涉及到 target$1.$on 和 target$1.$off
Vue.prototype.$on = function (event, fn) {
const vm = this;
if (isArray(event)) {
for (let i = 0, l = event.length; i < l; i++) {
vm.$on(event[i], fn);
}
}
else {
(vm._events[event] || (vm._events[event] = [])).push(fn);
// optimize hook:event cost by using a boolean flag marked at registration
// instead of a hash lookup
if (hookRE.test(event)) {
vm._hasHookEvent = true;
}
}
return vm;
};
else之前的代码都很简单,先缓存this,如果传入的事件是事件数组的话,则分别对数组内的每一项调用$on绑定事件
else之前的代码 如果event不是个数组,给vm._events[event]传入fn函数
if (hookRE.test(event)) {
vm._hasHookEvent = true;
}
它表示的是父组件有没有直接绑定钩子函数在当前组件上
return vm
最后 返回vm实例对象
现在我们知道了vm.$on方法主要就是把传入的方法给push到_events属性里,方便之后被$emit调用
Vue.prototype.$off = function (event, fn) {
const vm = this;
// all
if (!arguments.length) {
vm._events = Object.create(null);
return vm;
}
// array of events
if (isArray(event)) {
for (let i = 0, l = event.length; i < l; i++) {
vm.$off(event[i], fn);
}
return vm;
}
// specific event
const cbs = vm._events[event];
if (!cbs) {
return vm;
}
if (!fn) {
vm._events[event] = null;
return vm;
}
// specific handler
let cb;
let i = cbs.length;
while (i--) {
cb = cbs[i];
if (cb === fn || cb.fn === fn) {
cbs.splice(i, 1);
break;
}
}
return vm;
};
const vm = this;
// all
if (!arguments.length) {
vm._events = Object.create(null);
return vm;
}
从代码我们看出来当arguments.length为0的时候,说明没有任何参数,这时就需要移除所有的事件监听器,因此我们重置了 vm.events 属性。
// array of events
if (isArray(event)) {
for (let i = 0, l = event.length; i < l; i++) {
vm.$off(event[i], fn);
}
return vm;
}
如果参数event是个数组,则循环调用vm.$off
分析特殊情况
const cbs = vm._events[event];
if (!cbs) {
return vm;
}
if (!fn) {
vm._events[event] = null;
return vm;
}
表示,有event但是没有cbs,没有callbacks,没有回调函数
则vm._events[event] = null,返回vm实例对象
let cb;
let i = cbs.length;
while (i--) {
cb = cbs[i];
if (cb === fn || cb.fn === fn) {
cbs.splice(i, 1);
break;
}
}
return vm;
事件名和回调都有,从从列表中找到与参数提供回调函数相同的那个函数,并将它从列表中移除
再回到updateListeners方法
function updateListeners(on, oldOn, add, remove, createOnceHandler, vm) {
let name, cur, old, event;
for (name in on) {
cur = on[name];
old = oldOn[name];
event = normalizeEvent(name);
if (isUndef(cur)) {
warn$2(`Invalid handler for event "${event.name}": got ` + String(cur), vm);
}
else if (isUndef(old)) {
if (isUndef(cur.fns)) {
cur = on[name] = createFnInvoker(cur, vm);
}
if (isTrue(event.once)) {
cur = on[name] = createOnceHandler(event.name, cur, event.capture);
}
add(event.name, cur, event.capture, event.passive, event.params);
}
else if (cur !== old) {
old.fns = cur;
on[name] = old;
}
}
for (name in oldOn) {
if (isUndef(on[name])) {
event = normalizeEvent(name);
remove(event.name, oldOn[name], event.capture);
}
}
}
for (name in on) {
cur = on[name];
old = oldOn[name];
event = normalizeEvent(name);
if (isUndef(cur)) {
warn$2(`Invalid handler for event "${event.name}": got ` + String(cur), vm);
}
else if (isUndef(old)) {
if (isUndef(cur.fns)) {
cur = on[name] = createFnInvoker(cur, vm);
}
if (isTrue(event.once)) {
cur = on[name] = createOnceHandler(event.name, cur, event.capture);
}
add(event.name, cur, event.capture, event.passive, event.params);
}
else if (cur !== old) {
old.fns = cur;
on[name] = old;
}
}
遍历on中对象
如果on[name]未定义,则调用警告warn$2
如果oldOn[name]未定义,如果原事件 没有定义,则调用 createFnInvoker() 进行定义;但如果是一个 .once 修饰的事件,则会用 createOnceHandler() 重新定义该事件;最后将其添加到当前实例的 $on 属性中
如果on[name]与oldOn[name]不一致,将原事件定义的 函数定义部分 重新指向为当前新的事件定义,并赋值给新事件对象中
for (name in oldOn) {
if (isUndef(on[name])) {
event = normalizeEvent(name);
remove(event.name, oldOn[name], event.capture);
}
}
遍历oldOn中对象,将不存在于新事件对象中的事件去除