snabbdom源码剖析(七) 事宜处置惩罚

事宜处置惩罚

我们在运用 vue 的时刻,相信你肯定也会对事宜的处置惩罚比较感兴趣。 我们经由过程 @click 的时刻,究竟是发生了什么呢!

虽然我们用 @click绑定在模板上,不过事宜严厉绑定在 vnode 上的 。

eventlisteners 这个模块,就是定义了一些钩子,在 patch 的时刻,可以举行事宜的绑定以及解绑。

发起浏览这个篇章之前,先浏览
模块 相识简朴的模块以后,再回来

eventlisteners 模块

起首我们看下暴露出来的内容:

// 导出时候监听模块,建立、更新、烧毁
export const eventListenersModule = {
    create: updateEventListeners,
    update: updateEventListeners,
    destroy: updateEventListeners
} as Module;

这里我们可以晓得,在 createupdatedestroy 的时刻,便会触发 ,挪用 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

实行相应的事宜处置惩罚顺序。

重如果处置惩罚几种状况:

  1. handler 为函数的状况
  2. handlerobject , 然则第一个元素为 function 的状况 ,eg: handler = [fn,arg1,arg2] ;
  3. handlerobject ,第一个元素不为 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 来作为一致的事宜吸收, 更轻易的对事宜绑定以及解绑举行处置惩罚 ,在元素建立的时刻绑定事宜, 在烧毁的时刻解绑事宜,防备内存泄漏。 这类处理体式格局也是相称文雅,值得进修 :)

snabbdom源码剖析系列

snabbdom源码剖析(一) 准备工作

snabbdom源码剖析(二) h函数

snabbdom源码剖析(三) vnode对象

snabbdom源码剖析(四) patch 要领

snabbdom源码剖析(五) 钩子

snabbdom源码剖析(六) 模块

snabbdom源码剖析(七) 事宜处置惩罚

个人博客地址

    原文作者:chen4342024
    原文地址: https://segmentfault.com/a/1190000017519415
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞