【Vue原理】Event - 源码版 之 绑定标签DOM事件

写文章不容易,点个赞呗兄弟
专注 Vue 源码分享,文章分为白话版和 源码版,白话版助于理解工作原理,源码版助于了解内部详情,让我们一起学习吧
研究基于 Vue版本 【2.5.17】

如果你觉得排版难看,请点击 下面链接 或者 拉到 下面关注公众号也可以吧

【Vue原理】Event - 源码版 之 绑定标签DOM事件

这里的绑定DOM事件,是指绑定原生标签的DOM 事件

因为组件也是可以绑定原生DOM事件的,不过并不是在原生标签上绑定,而是直接在组件上绑定的,这部分内容会其他文章说明

或者你可以看看白话版先了解下Event

【Vue原理】Event - 白话版


怎么解析

由于解析不是本内容的重点,所以在这里就不谈怎么解析的了,只说一个结果就好了

现在有这么一个模板

模板被解析成这样的渲染函数

渲染函数执行之后,得到这样的 VNode

你可以看到,事件被存放到了 vnode.data 上

Vnode 有疑惑的可以看介里

【Vue原理】VNode - 源码版


怎么绑定

既然模板已经被解析完成了,下一步就是开始绑定了

好的,继续来走流程

在 template 解析得到 Vnode 之后,下面就会进行DOM生成挂载

而绑定事件,就发生在开始挂载,创建DOM 之后 的阶段

挂载时从 Vue.prototype._update 这个函数开始的

挂载的流程,可以看看这篇文章

从模板到DOM的简要流程

1、开始挂载

VNode创建完毕,传入 Vue.prototype._update 这个方法中,进行比对新旧VNode

然后生成DOM挂载页面

其中需要生成DOM,调用的方法是 createElm

2、创建DOM

创建DOM,在Vue 中调用的是 createElm 这个方法

看过以前的文章的,都知道这个函数的作用是

根据 vnode 生成DOM,并且进行挂载

而在 createElm 中,会调用一个函数去 处理模板上相关的数据

比如处理属性,类名,style 之类的,其中DOM事件也是在这里处理的

这个函数就是 invokeCreateHooks,继续往下看

function createElm(vnode) {    

    // ....处理组件

    // ....生成标签对应dom

    // ....递归遍历子节点

    invokeCreateHooks(vnode);    

    // ....插入DOM 节点

}

3、处理数据

上面源码中出现的 invokeCreateHooks 这个方法是用来处理数据的

每种数据(style,class等),都有一个专门的函数去进行处理

而 invokeCreateHooks,就是负责执行每种数据的处理函数,很简单,就是一个单纯遍历执行的过程

其中就包括处理 DOM 事件的函数,便是 updateDOMListeners

function invokeCreateHooks(

    vnode

) {    

    /**

     * 执行的函数包括下面这么多
     * cbs = [
     *  create:[
     *      updateAttrs, updateClass,
     *      updateDOMListeners, updateDOMProps,
     *      updateStyle, create, updateDirectives
     *  ]
     * ]

     **/
    for (var b = 0; b < cbs.create.length; ++b) {        

        // 其中会调用 updateDOMListeners

        // emptyNode 是空节点,因为这里是初始化才会调用的

              // 所以旧节点是空节点

        cbs.create[b](emptyNode, vnode);
    }
    ....

}

下面看下 处理DOM 事件的函数

4、绑定DOM事件

简化的源码,看起来顺眼多了,主要逻辑一清二楚,主要就是绑定事件和解绑事件,你看下喽,挺简单的

function updateDOMListeners(oldVnode, vnode) {  

    var on = vnode.data.on || {};    

    var oldOn = oldVnode.data.on || {};

    var target = vnode.elm;
    

    // 遍历绑定的事件

    for (name in on) {

        newHandler = on[name];

        oldHandler = oldOn[name];   
     
        // 没有旧事件,就直接添加新事件

        if (typeof oldHandler === "undefined") {
          
            // 给事件回调包装一层

            target.addEventListener(name, function(){

                on[name]() // 执行保存在vnode的事件

            });

        }        
        // 新事件和旧事件不一样,替换旧事件

        else if (newHandler !== oldHandler) {
            on[name] = newHandler;
        }
    }   

    // 移除旧事件

    for (name in oldOn) {   

        // 旧事件不存在新事件中,直接移除  

        if (typeof on[name] === "undefined") {

            target.removeEventListener(
                name, oldOn[name]
            );

        }
    }

}

看看绑定函数和 移除函数,就只是简单使用 addEventListener 和 removeEventListener,我没看之前还以为 Vue 写了很多兼容,没想到就是这么简单完成这个功能

有点惊讶,反正简单也好吧,哈哈哈,简单看着就是苏胡啊~~

绑定逻辑很简单

1、新旧事件相同,替换旧事件

2、新事件不存在旧事件中,绑定新事件

3、旧事件不存在新事件中,解绑旧事件

其中会给回调事件函数包装一层函数,然后在内部执行绑定的回调,包装一层的原因是,为了在回调中做点其他操作(比如宏微任务的处理等,这里为了简单去掉了)

并且旧事件回调改了的时候,就更加方便了,不用解绑再绑定,直接把执行的事件回调 on[name]替换掉就ok了

好的,原生标签绑定DOM 事件到这里就完成了,希望对大家有所帮助

你可能感兴趣的:(javascript,vue.js,前端,前端框架)