事宜处置惩罚
我们在运用 vue
的时刻,相信你肯定也会对事宜的处置惩罚比较感兴趣。 我们经由过程 @click
的时刻,究竟是发生了什么呢!
虽然我们用 @click
绑定在模板上,不过事宜严厉绑定在 vnode
上的 。
eventlisteners
这个模块,就是定义了一些钩子,在 patch
的时刻,可以举行事宜的绑定以及解绑。
发起浏览这个篇章之前,先浏览
模块 相识简朴的模块以后,再回来
eventlisteners 模块
起首我们看下暴露出来的内容:
// 导出时候监听模块,建立、更新、烧毁
export const eventListenersModule = {
create: updateEventListeners,
update: updateEventListeners,
destroy: updateEventListeners
} as Module;
这里我们可以晓得,在 create
、 update
、 destroy
的时刻,便会触发 ,挪用 updateEventListeners
;
接下来我们来细致相识下 updateEventListeners
;
updateEventListeners
浏览之前加两个小的知识点,有助于明白
-
vnode.data.on
: 这个保留了一系列的绑定事宜。 比方 on[‘click’] ,内里保留了绑定的 click 事宜 -
vnode.listener
: 作为现实绑定到元素上的回调 。elm.addEventListener(name, listener, false);
。一切的事宜触发后都是先回调到listener
,再分发给差别的事宜处置惩罚器
updateEventListeners
函数的重要逻辑以下 :
- 删除新事宜列表上不存在的事宜
- 增加新增的事宜
/**
* 更新事宜监听器
*/
function updateEventListeners(oldVnode: VNode, vnode?: VNode): void {
var oldOn = (oldVnode.data as VNodeData).on,
oldListener = (oldVnode as any).listener,
oldElm: Element = oldVnode.elm as Element,
on = vnode && (vnode.data as VNodeData).on,
elm: Element = (vnode && vnode.elm) as Element,
name: string;
// optimization for reused immutable handlers
if (oldOn === on) {
return;
}
// remove existing listeners which no longer used
// 删除过剩的事宜
if (oldOn && oldListener) {
// if element changed or deleted we remove all existing listeners unconditionally
if (!on) {
// 假如新的节点没有绑定事宜,则删除一切的事宜
for (name in oldOn) {
// remove listener if element was changed or existing listeners removed
// 删除监听器
oldElm.removeEventListener(name, oldListener, false);
}
} else {
for (name in oldOn) {
// remove listener if existing listener removed
// 删除在新事宜列表上不存在的监听器
if (!on[name]) {
oldElm.removeEventListener(name, oldListener, false);
}
}
}
}
// add new listeners which has not already attached
if (on) {
// reuse existing listener or create new
// 重用老的监听器
var listener = ((vnode as any).listener =
(oldVnode as any).listener || createListener());
// update vnode for listener
listener.vnode = vnode;
// if element changed or added we add all needed listeners unconditionally
if (!oldOn) {
for (name in on) {
// add listener if element was changed or new listeners added
elm.addEventListener(name, listener, false);
}
} else {
for (name in on) {
// add listener if new listener added
// 增加新增的监听器
if (!oldOn[name]) {
elm.addEventListener(name, listener, false);
}
}
}
}
}
createListener
这里我们看到,事宜触发以后都邑先回调到 listener ,那它是怎样回调的呢。
起首看下建立 listener
/**
* 建立监听器
*/
function createListener() {
// 事宜处置惩罚器
return function handler(event: Event) {
handleEvent(event, (handler as any).vnode);
};
}
handleEvent
当事宜触发的时刻,会挪用 handleEvent(event, (handler as any).vnode);
handleEvent
重要担任转发 , 去除 on 内里对应的事宜处置惩罚函数,举行挪用
// 处置惩罚事宜
function handleEvent(event: Event, vnode: VNode) {
var name = event.type,
on = (vnode.data as VNodeData).on;
// call event handler(s) if exists
// 假如存在回调函数,则挪用对应的函数
if (on && on[name]) {
invokeHandler(on[name], vnode, event);
}
}
invokeHandler
实行相应的事宜处置惩罚顺序。
重如果处置惩罚几种状况:
-
handler
为函数的状况 -
handler
为object
, 然则第一个元素为function
的状况 ,eg:handler = [fn,arg1,arg2]
; -
handler
为object
,第一个元素不为function
的状况 , eg:handler = [[fn1,arg1],[fn2]]
/**
* 挪用事宜处置惩罚
*/
function invokeHandler(handler: any, vnode?: VNode, event?: Event): void {
if (typeof handler === 'function') {
// call function handler
// 函数状况下直接挪用
handler.call(vnode, event, vnode);
} else if (typeof handler === 'object') {
// call handler with arguments
if (typeof handler[0] === 'function') {
// handler为数组的状况。 eg : handler = [fn,arg1,arg2]
// 第一项为函数申明背面的项为想要传的参数
// special case for single argument for performance
if (handler.length === 2) {
// 当长度为2的时刻,用call,优化机能
handler[0].call(vnode, handler[1], event, vnode);
} else {
// 组装参数,用 apply 挪用
var args = handler.slice(1);
args.push(event);
args.push(vnode);
handler[0].apply(vnode, args);
}
} else {
// call multiple handlers
// 处置惩罚多个handler的状况
for (var i = 0; i < handler.length; i++) {
invokeHandler(handler[i]);
}
}
}
}
小结
这里经由过程 listener 来作为一致的事宜吸收, 更轻易的对事宜绑定以及解绑举行处置惩罚 ,在元素建立的时刻绑定事宜, 在烧毁的时刻解绑事宜,防备内存泄漏。 这类处理体式格局也是相称文雅,值得进修 :)