本来没想整理的,基本上是用到的时候现查为主。
然后刷到一个面试题要求随便举几个例子,虽然磕磕绊绊的说出来了一些,但是这实在不符合一个老前端应有的知识储备。
索性这里统一整理下,反正也不是太麻烦的事情。
正文
事件修饰符Vue框架本身提供的事件语法糖,无论是V2还是V3,大部分都是通用的。
这里就不详细去分析他们的原理,仅收集供面试和平时开发时使用。
事件修饰符
在正式进入事件修饰符事件之前,我们需要先简单了解一些概念,不然新人直接看这些修饰符可能会懵。
事件相关内容委托
众所周知,我们平时写的页面是由DOM树不断嵌套堆叠而成的,当我们与DOM交互的时候,实际上是要穿透这一层层DOM结构,触发到对应节点的事件。
早年这里会考个面试题,如果每层节点都绑定事件,那么这是这每层事件的执行顺序是怎样的?
1 | <!DOCTYPE html> |
看到这个结果,我们可能会疑惑,为什么最外层的绑定的事件反而最后才触发?
由此,我们需要明白两个基础概念:事件冒泡 与 事件委托。
事件冒泡
事件冒泡(dubbed bubbling)
:当一个元素接收到事件的时候,会把他接收到的事件传给自己的父级,一直到 window
(注意这里传递的仅仅是事件,例如click、focus
等等这些事件, 并不传递所绑定的事件函数。)
事件源 =>根节点(由内到外)进行事件传播。
所以我们会发现,这次的事件触发结果,实际上就是由内而外的执行。
事件捕获
事件捕获(event capturing)
: 当鼠标点击或者触发dom
事件时(被触发dom
事件的这个元素被叫作事件源),浏览器会从根节点 =>事件源(由外到内)进行事件传播。
事件捕获与事件冒泡是比较类似的,最大的不同在于事件传播的方向。
这里我们不再用vue讨巧举例了,因为vue的事件本质上就是封装了事件注册方法:addEventListener('click',() =>{}, false)
。
事件注册:addEventListener
,通过控制最后一个传值,我们就能决定触发的方向是由内向外还是由外向内。
我们将三个div,由外到内命名为:big,center,small,再看一结果。
1 | big.addEventListener('click',()=>{ |
这里我们就会发现,两者的触发方向完全不一样。
- 事件冒泡:由内向外。
- 事件捕获:由外向内。
阻止事件传播
当点击页面的表格内的按钮时候,有时候我们会触发行点击事件,这时候我们希望仅仅点击按钮,那么我们必然要阻止这种事件传播的问题。
阻止事件传播,常用的有两种方法:
event.stopPropagation()
阻止事件传播,不会
阻止同一元素上的其他的事件处理程。event.stopImmediatePropagation()
阻止事件传播,会
阻止同一元素上的其他的事件处理程。
当然也不仅仅局限于此,有时候,表单被提交时默认的submit事件,我们不希望触发,这里我们也可以通过这种方式阻止这些默认事件。
事件委托
事件委托
也称为事件代理
。
就是利用事件冒泡
,把子元素的事件都绑定到父元素上。
如果子元素阻止了事件冒泡,那么委托就无法实现。
1 | 不是每个子节点单独设置事件监听器,而是事件监听器设置在其父节点上,然后利用冒泡原理影响设置每个子节点。 |
例子
现在要为每一个li
添加一个事件,假设li
有100个,于是就需要为每一个li
添加一个事件,这样会占用100个内存。
因此,如果使用事件委托的话,可以利用事件的冒泡机制,为ul
绑定一个事件,那么点击任意一个li
的时候,都会将事件触发到父元素ul
上。
1 | <ul> |
代码展示
1 | <ul id="ul"> |
可以使用event.target
获取到点击的元素,event.target.innerHTML
获取到点击的元素的内容。 如下代码,当你点击li
时,会添加一个红色的背景,再次点击,会将背景变为白色。
1 | const ulE = document.querySelector('#ul') |
简单的来说,就是事件的目标源委托给父级元素,减少性能的损耗。
故此,使用事件代理/委托
可以提高性能,减少注册的事件。
v-on
事件修饰符
vue官方提供了很多处理事件的修饰符,主要我们平时很多用不到,所以并未深入了解。
我这里简单的按照使用频率,将事件修饰符分为通用修饰符和特殊修饰符,特殊修饰符我会单独讲一下它们的应用场景。
通用修饰符
这些修饰符有些在vue2中,Vue3就没有了相应的修饰符,所以这里会备注一下支持的版本。
修饰符名称 | 支持版本 | 事件 | 备注 |
---|---|---|---|
.native |
v2 | 监听组件根元素的原生事件 | |
.prevent |
v2,v3 | 阻止事件的默认动作 | 比如表单默认的submit按钮会刷新页面,我们用这个就可以阻止刷新页面 |
.once |
v2,v3 | 仅触发一次 | 可用来避免用户重复点击导致多次提交表单 |
.stop |
v2,v3 | 阻止事件传播/冒泡 | 避免事件冒泡触发了父元素的方法 |
.self |
v2,v3 | 只当事件是从侦听器绑定的元素本身触发时才触发回调。 | 通过事件委托的形式,减少页面性能小号 |
.passive |
v2,v3 | 滚动事件的默认行为 (scrolling) 将立即发生而非等待 onScroll 完成 |
移动端常用处理滚动监听,PC端很少用 |
.capture |
v2,v3 | 指向内部元素的事件,在被内部元素处理前,先被外部处理 | 采用了捕获的方式,可以调整执行顺序 |
使用修饰符时需要注意调用顺序,因为相关代码是以相同的顺序生成的。
因此使用
@click.prevent.self
会阻止元素及其子元素的所有点击事件的默认行为。而
@click.self.prevent
则只会阻止对元素本身的点击事件的默认行为。
.passive修饰符
.passive
修饰符一般用于触摸事件的监听器,可以用来改善移动端设备的滚屏性能,详情参考CSDN的这篇:Vue事件处理:.passive修饰符与应用场景
请勿同时使用
.passive
和.prevent
,因为.passive
已经向浏览器表明了你不想阻止事件的默认行为。如果你这么做了,则
.prevent
会被忽略,并且浏览器会抛出警告。
键盘事件
.{keycode | keyAlias}
键盘中每个按键都有自己对应的keycode,vue提供的语法糖,让我们可以直接使用对应事件绑定,也可以用对应的keycode绑定。
这里,我们以常用的回车事件为例子,我们按键加入enter修饰符,代表我们点击回车时,可以触发test()
事件。
1 | <el-input @keypress.enter="test()"> |
这里我们就可以发现,使用keycode和修饰符都可以达到对应的效果。
vue官方提供了很多便利性的事件,如果您需要将方法绑定对应的按钮,不妨参考MDN的KeyCode码表,这样绑定会方便很多。
修饰符名称 | 事件 |
---|---|
.enter |
回车 |
.delete |
Delete或Backspace |
.ctrl |
ctrl按键 |
.alt |
alt按键 |
.shift |
shift按键 |
.tab |
tab按键 |
.esc |
esc按键 |
.space |
space空格按键 |
.up ,.down ,.left ,.right |
上下左右方向键 |
.meta |
win键/mac的commond键 |
[.exact ]( <button @click.ctrl=”onClick”>A <button @click.ctrl.exact=”onCtrlClick”>A <button @click.exact=”onClick”>A) |
修饰符允许精确控制触发事件所需的系统修饰符的组合。 |
注意:如果多个组件注册了键盘事件,最好销毁,保证键盘事件的唯一性,不然有时候会导致整个页面出现一些意料之外的问题。
另外,有人可能对exact的描述有些迷惑,这里放一下官方的例子,一看就明白,就是为了精确操作使用的。
1 | <!-- 当按下 Ctrl 时,即使同时按下 Alt 或 Shift 也会触发 --> |
鼠标
@click
鼠标事件,没什么好说的。
修饰符名称 | 事件 |
---|---|
.left |
左键 |
.right |
右键 |
.middle |
中间 |
属性修饰符
v-bind
v-bind的修饰符我们几乎很少用,这里推荐大家看看,知道有这么回事儿就行。
修饰符名称 | 备注 |
---|---|
.prop |
强制绑定为 DOM property |
.attr |
强制绑定为 DOM attribute |
.camel |
将短横线命名的 attribute 转变为驼峰式命名 |
.sync |
vue2支持,vue3不支持了。 |
这里直接放一下官方的示例。
1 | <!-- 绑定 attribute --> |
v-model
修饰符名称 | 备注 |
---|---|
.trim |
去除输入框首尾字符串 |
.number |
将用户输入的类型由string型转为number型 |
.lazy |
使得用户在输入数据之后,当数据失去焦点或点击回车时,才会进行数据的更新 |
结语
事件修饰符平时用的不多,最多就是输入框判空,表单单次提交,键盘回车等常用事件的使用,平时没有别的用法。
关于事件冒泡和事件捕获,这个就更是远古面试题了,如今在梳理修饰符时候刷到,属实有点老乡见老乡了。
如今仔细梳理了一番,感觉自己确实受益匪浅。
虽然依然用的场景可能不太多,但是相对以前,可能在开发中会更得心应手一些?