一、问题描述
今天看到一个问题,在用Vue2+element-ui 2.15.8开发时,使用input组件绑定keydown事件没有任何效果。
<template>
<div id="app">
<el-input v-model="content" placeholder="请输入" @keydown="handelKeydown"/>
</div>
</template>
<script>
export default {
data() {
return {
content: ''
}
},
methods: {
handelKeydown() {
console.log('触发了Keydown事件!!!') // 实际不会触发
}
}
}
</script>
二、问题排查
我们直接调试代码,找到Vue2中事件初始化的地方initEvents
:
可以看到,我们的keydown事件其实是有记录到的,那么事件绑定的关键就在updateComponentListeners
:
在这里有两句代码很关键:
① target$1 = vm;
,target$1
这个变量后面会提到,它就是vm,我们<el-input>组件的实例对象。
② updateListeners
:updateComponentListeners
实际内部调用的事件更新处理,它里面如何执行,我们往下看:
可以看到,updateListeners
里实现事件绑定的逻辑是在add
函数内:
在这里我们再次看到了 target$1
变量,从而keydown事件其实就是绑定在它上面了。
那么如果要使得keydown事件能够正常触发,<el-input>组件内部在<input>上应该要有@keydown
,并$emit('keydown')
。于是,到<el-input>组件源码内看看到底有没有这个:
可以看到<el-input>组件并没有绑定keydown事件。
接下来,修改一下它的源码来验证一下:
这时候不要直接去调试,如果直接修改源码后调试会发现没有任何效果,即使你重新启动服务。原因在于,加载的Element-UI目标文件并不是源码文件:
也就是,在main.js
中使用的import Element from 'element-ui'
导入的其实是node_modules\element-ui\lib\element-ui.common.js
所以这里我们还要再修改一下main.js
:
将import Element from 'element-ui'
替换成下面的代码:
import Element from 'element-ui/src/index'
最后我们yarn serve
重启一下服务,来验证一下:
三、解决方案
在实际开发中,不用去修改element-ui的源码也能使keydown事件生效,这里需要用到Vue的事件修饰符:navtive
修改一下代码:
<el-input v-model="content" placeholder="请输入" @keydown.native="handelKeydown"/>
同样可以看到它生效了。
这里你可能有疑问,native
为何能使keydown生效了呢?我们简单看一下:
可以看到:
给普通vnode创建完dom后、和createChilren后,会调用invokeCreateHooks函数,这里面会执行属性、事件、指令等的create钩子函数(注意不是组件实例的create钩子函数)。
在事件的create钩子函数中,会调用updateDomLIsteners方法的updateListeners方法,因为是创建阶段,所以又会调用add方法,使用target.addEventListeners给目标真实dom元素添加监听事件。
另:
在最后调试的过程中,在invokeCreateHooks
函数内,你可能会好奇:cbs.create[i_2]
指向updateDOMListeners
,这个是怎么来的呢?
关于这个其实来自于下面的代码: