Vue组件是如何进行处理v-on/@定义的事件的

本文探讨Vue组件如何处理v-on/@定义的事件,从将template上的事件绑定转化为AST,到动态生成函数,再到VNode中事件监听的添加与删除。重点关注事件分类,如浏览器内置事件与内部事件系统,以及不同修饰符对事件处理的影响,揭示了框架如何增强事件监听功能。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

我们在使用Vue框架的时候,最亲切的感觉是莫过于它将组件开发中的HTML、CSS、JS代码分割开来,保持着原生开发的习惯。并且template语法提供比原生更加强大的功能,对于事件监听来说,他提供@click.left@click.preventDefault等等修饰方法,有没有想过框架的内部是如何实现的呢?下面就来讲解,尽量少讲代码,多讲流程和方法,因为这一部分代码就很多中情况考虑的多,多讲代码反而不好。

将template上面的事件绑定转为AST

Vue框架对template处理的流程是:

  1. 词法解析:词法分析即对template语法进行判断是否使用不当,比如一些标签只能作为单标签,一些标签只能作为双标签,有些两者都能够使用。如果用户使用出错的话会在这一层提示。
  2. 句法解析:基于语法分析进行的,对于键值对等信息进行分析,如一些动态的属性,就进行动态属性处理,一些静态属性交由静态属性处理器处理。
  3. 生成抽象语法树:将上述处理的结果生成抽象语法树,抽象语法树的基本单位是标签表达式普通文本,生成树状结构,代表着代码的嵌套结构。
  4. 由抽象语法树生成渲染函数,并且交由渲染函数观察者来使用。

将AST上面事件进行动态生成函数(重点)

在句法分析的时候,会对@click.left="handler"进行处理,会进行分类:

  1. 浏览器内置事件,指的是我们原生使用到的那些事件,比如说clickinput等等
  2. Vue内部事件系统,即我们对组件自定义的事件。$emit、$on什么的。

接下来我们重点来讲解普通的事件

事件分为eventnativeEventevent针对于普通的标签,而nativeEvent针对于组件化的标签(对于组件的第一个元素进行事件监听)。不过两者的区别不是很大,我们拿events来进行讲解:

首先我们写一个例子:

span标签进行添加事件,那么抽象语法树的节点的样子如下:

模板编译器会将我们写进去的修饰放在modifiers中,以便后面将抽象语法树转为代码的时候进行使用。然后我们将目光看到compiler/codegen/events中,如下图

看到这里,我们会知道里面的字符串其实是作为代码来使用的,所以我们会有预感:最后会使用new Function生成一个函数,然后这个函数使用了上面的代码。接下来看到本文件的genHandler函数,它是整合成代码的主方法。

function genHandler (handler: ASTElementHandler | Array<ASTElementHandler>): string {
  if (!handler) {
    return 'function(){}'
  }

  if (Array.isArray(handler)) {
    return `[${handler.map(handler => genHandler(handler)).join(',')}]`
  }

  const isMethodPath = simplePathRE.test(handler.value)
  const isFunctionExpression = fnExpRE.test(handler.value)
  const isFunctionInvocation = simplePathRE.test(handler.value.replace(fnInvokeRE, ''))

  if (!handler.modifiers) {
    // 没有修饰符的情况,直接返回调用的函数名
    // code...
  } else {
    // 有修饰符的情况下,先执行修饰符的代码,后再执行handler代码,所以封装成一个函数返回
    // code...
    return `function($event){${code}${handlerCode}}`
  }
}

如果没有修饰符的时候,是不会多一层函数来封装用户的回调方法的,而是直接调用用户的方法。这样就跟普通的时间绑定一致。但是有了修饰符,就会先执行修饰符产生的代码,还记得前面的图片展示了的判断代码吗?它就是来将修饰符转为代码的,接下来我们翻译翻译我们定义的事件处理后的代码吧!

function($event) {
  $event.stopPropagation();
  $event.preventDefault();
  
  if (!$event,altKey) {
    return null;
  }
  
  sayName($event);
}

我们可以处理成上面的方法。所以如果有进行修饰的话,事件处理函数不是我们所看到的回调函数,而是封装了一层函数,框架在这个函数对事件执行了禁止冒泡、阻止默认事件,以及当事件没有altKey这个属性的时候,不会执行,这就使得模板语法有了更强大的功能。而在函数末尾进行调用用户规定的回调函数。(在框架内部处理完毕是一个字符串,然后通过with(this)来延长作用域链,所以直接访问sayName是找得到方法)。

VNode进行事件监听的添加或者删除

对于普通的事件,最后是要添加到真实的dom节点上面的,所以在VNode进行渲染的时候,会进行节点的添加,在src/platforms/web/runtime/modules/events.js这里会有方法进行节点事件的添加。updateListeners采取的新旧事件监听更新的代码也很容易理解,这里就不进行讲解。不过要明白对普通dom节点的事件监听是在VNode进行创建或者更新的时候,而对于组件的事件监听的话,后续会补上。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值