Vue事件绑定原理
Vue事件绑定原理
Vue中通过v-on或其语法糖@指令来给元素绑定事件并且提供了事件修饰符,基本流程是进行模板编译生成AST,生成render函数后并执行得到VNode,VNode生成真实DOM节点或者组件时候使用addEventListener方法进行事件绑定。
描述
v-on与@用于绑定事件监听器,事件类型由参数指定,表达式可以是一个方法的名字或一个内联语句,如果没有修饰符也可以省略,用在普通元素上时,只能监听原生DOM事件,用在自定义元素组件上时,也可以监听子组件触发的自定义事件,在监听原生DOM事件时,方法以事件为唯一的参数,如果使用内联语句,语句可以访问一个$event property:v-on:click="handle('param', $event)",自2.4.0开始v-on同样支持不带参数绑定一个事件或监听器键值对的对象,注意当使用对象语法时,是不支持任何修饰器的。
修饰符
.stop: 调用event.stopPropagation(),即阻止事件冒泡。.prevent: 调用event.preventDefault(),即阻止默认事件。.capture: 添加事件侦听器时使用capture模式,即使用事件捕获模式处理事件。.self: 只当事件是从侦听器绑定的元素本身触发时才触发回调。.{keyCode | keyAlias}: 只当事件是从特定键触发时才触发回调。.native: 监听组件根元素的原生事件,即注册组件根元素的原生事件而不是组件自定义事件的。.once: 只触发一次回调。.left(2.2.0): 只当点击鼠标左键时触发。.right(2.2.0): 只当点击鼠标右键时触发。.middle(2.2.0): 只当点击鼠标中键时触发。.passive(2.3.0): 以{ passive: true }模式添加侦听器,表示listener永远不会调用preventDefault()。
普通元素
1 | <!-- 方法处理器 --> |
组件元素
1 | <!-- 自定义事件 --> |
分析
Vue源码的实现比较复杂,会处理各种兼容问题与异常以及各种条件分支,文章分析比较核心的代码部分,精简过后的版本,重要部分做出注释,commit id为ef56410。
编译阶段
Vue在挂载实例前,有相当多的工作是进行模板的编译,将template模板进行编译,解析成AST树,再转换成render函数,而在编译阶段,就是对事件的指令做收集处理。
在template模板中,定义事件的部分是属于XML的Attribute,所以收集指令时需要匹配Attributes以确定哪个Attribute是属于事件。
1 | // dev/src/compiler/parser/index.js line 23 |
通过addHandler方法,为AST树添加事件相关的属性以及对事件修饰符进行处理。
1 | // dev/src/compiler/helpers.js line 69 |
代码生成
接下来需要将AST语法树转render函数,在这个过程中会加入对事件的处理,首先模块导出了generate函数,generate函数即会返回render字符串,在这之前会调用genElement函数,而在上述addHandler方法处理的最后执行了el.plain = false,这样在genElement函数中会调用genData函数,而在genData函数中即会调用genHandlers函数。
1 | // dev/src/compiler/codegen/index.js line 42 |
可以看到无论是处理普通元素事件还是组件根元素原生事件都会调用genHandlers函数,genHandlers函数即会遍历解析好的AST树中事件属性,拿到event对象属性,并根据属性上的事件对象拼接成字符串。
1 | // dev/src/compiler/codegen/events.js line 3 |
事件绑定
前面介绍了如何编译模板提取事件收集指令以及生成render字符串和render函数,但是事件真正的绑定到DOM上还是离不开事件注册,此阶段就发生在patchVnode过程中,在生成完成VNode后,进行patchVnode过程中创建真实DOM时会进行事件注册的相关钩子处理。
1 | // dev/src/core/vdom/patch.js line 33 |
invokeCreateHooks就是一个模板指令处理的任务,他分别针对不同的指令为真实阶段创建不同的任务,针对事件,这里会调updateDOMListeners对真实的DOM节点注册事件任务。
1 | // dev/src/platforms/web/runtime/modules/events.js line 105 |
最终添加与移除事件都是调用的add与remove方法,最终调用的方法即DOM的addEventListener方法与removeEventListener方法。
1 | // dev/src/platforms/web/runtime/modules/events.js line 46 |
参考
1 | https://cn.vuejs.org/v2/api/#v-on |
