在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/propsinitState(vm);initProvide(vm); // resolve provide after data/propscallHook$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 eventsconst 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 lookupif (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;// allif (!arguments.length) {vm._events = Object.create(null);return vm;}// array of eventsif (isArray(event)) {for (let i = 0, l = event.length; i < l; i++) {vm.$off(event[i], fn);}return vm;}// specific eventconst cbs = vm._events[event];if (!cbs) {return vm;}if (!fn) {vm._events[event] = null;return vm;}// specific handlerlet 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中对象,将不存在于新事件对象中的事件去除