vue源码-事件添加@click

2,478 阅读3分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第9天,点击查看活动详情

前言

学习框架,仅仅会使用是不够的,想要更熟悉框架,需要对源码有一定的了解。本篇文章通过vue源码解析,实现vue的事件添加@click。

介绍

v-on,缩写@,参数event,绑定事件监听器。事件类型由参数指定。表达式可以是一个方法的名字或一个内联语句,如果没有修饰符也可以省略。 用在普通元素上时,只能监听原生 DOM 事件。

代码

<button @click='handle'>点击触发</button>
  
  <script>
    new Vue({
      el: '#app',
      methods: {
        handle(e) {
          console.log('111')
          console.log(e)
        }
      },
    })
  </script>

当按钮被点击,会触发methods中的handle()打印 '111',如果handle方法接收一个参数e,那么我们可以获得点击事件的参数。

image-20220605125734195

实现

在实现vue添加事件之前,先来看看原生js是如何实现事件监听的

事件监听的两种实现方法:

  • 标签中的onxxx,比如<button οnclick="btnHandler">按钮</button>
  • js中的addEventListener,比如document.body.addEventListener("click", () => {console.log('111')})

所以,vue中想要实现添加事件,只有两种情况:onclickaddEventListener

上述代码中,我们先分析DOMvue是如何知道点击按钮,要触发哪个方法,我们一步步来分析。

  • 在元素节点中判断是否有@click。

    vue中,@click永远是给元素节点加的。根据之前对模板的解析知道,元素节点对应的nodeType值为1

    我们只需要在nodeType值为1的节点中,判断是否有@click,即在元素节点中判断是否有@click。我们通过hasAttribute()方法判断

    补充:HTML DOM hasAttribute() 方法

    定义和用法
    hasAttribute() 方法用于判断是否有指定的属性存在,如果存在返回 true,否则返回 false。
    
    提示: 我们可以使用 setAttribute() 来添加一个新属性,或者修改元素中已存在的属性。
    
    实例:
    // 检查按钮元素是否有 onclick属性:
    document.getElementsByTagName("BUTTON")[0].hasAttribute("onclick"); // true
    

    代码

    index.html

      <div id="app">
        {{message}}
        <h1>{{name}}</h1>
        <h2>{{age}}</h2>
        <button @click='handle'>点击触发</button>
      </div>
    

    vue.js

    image-20220605132935077

    第一个文本节点不做判断,第二个和第三个标签节点没有@click,所以为false,第四个标签节点有@click,所以为true

  • 获取@click中的函数

    @click后,会写需要被执行的函数,我们需要在实例中,获取到@click对应的函数

    vue.js中,需要拿到vue对象,因为@click绑定的方法在vue对象中,即为options

    然后根据@click中的方法名,找到vue对象中,对应的方法

    代码

          // 元素节点
          if (item.nodeType == 1) {
            // 判断元素节点是否有@click
            if (item.hasAttribute('@click')) {
              console.log(this.$options.methods['handle'])
            }
            // 递归执行模板解析        
            this.Parser(item)
          }
    

    image-20220605133140910

    成功获取到@click绑定的函数,但是,获取的函数名是写死的,需要通过动态获取函数名

  • 动态查找@click绑定的方法名

    通过getAttribute('@click'),就能找到对应方法名

    实现的时候,尽量将两边空格去除

    补充:

    定义和用法
    getAttribute() 方法通过名称获取属性的值。
    
    提示:如果你想返回属性请使用 getAttributeNode 方法。
    
    实例:
    // 获取链接的 target 属性值:
    document.getElementsByTagName("a")[0].getAttribute("target"); // _blank
    

    代码

          // 元素节点
          if (item.nodeType == 1) {
            // 判断元素节点是否有@click
            if (item.hasAttribute('@click')) {
              console.log(item.getAttribute('@click').trim())
            }
            // 递归执行模板解析        
            this.Parser(item)
          }	
    

    image-20220605133741599

  • 为有@click的元素节点绑定点击事件

    上面已经分析过了,添加事件的两种形式,我们这里用到js添加事件的方法addEventListener,添加的为click事件,方法名为动态获取的方法。

    代码

          // 元素节点
          if (item.nodeType == 1) {
            // 判断元素节点是否有@click
            if (item.hasAttribute('@click')) {
              // 获取@click节点绑定的事件名
              let value = item.getAttribute('@click').trim()
              // 为存在@click的节点,添加事件
              item.addEventListener('click', () => {
                //执行事件
                this.$options.methods[value]()
              })
            }
    

    image-20220605134555222

    点击按钮,click事件成功被执行,但是事件对象e没有获取到

  • 获取事件对象e

    获取事件对象,只需要vue在执行click事件时,传递eventvue对象中的方法即可

    这里需要注意this指向

    代码

          // 元素节点
          if (item.nodeType == 1) {
            // 判断元素节点是否有@click
            if (item.hasAttribute('@click')) {
              // 获取@click节点绑定的事件名
              let value = item.getAttribute('@click').trim()
              // 为存在@click的节点,添加事件
              item.addEventListener('click', (e) => {
                //执行事件,修改this指向当前对象,并传递e
                this.$options.methods[value].bind(this)(e)
              })
            }
            // 递归执行模板解析        
            this.Parser(item)
          }
    

    image-20220605135240617

    成功执行事件,并获取到事件对象e

总结

vue中添加事件的原理并不难,只要从原生DOM中添加事件的方法出发,就能理清楚vue添加事件的原理。

实现思路是:vue通过模板解析从模板中找到绑定@click的元素节点,获取@click的方法名,再为此元素节点绑定一个click事件(通过addEventListener方法),并在vue对象中找到对应方法,传递事件对象,最后帮你执行。

最后,vue中还有很多事件处理方法,不过其他的事件处理的原理和@click也差不多,根据这个思路,就能实现。