2.4 事件处理
2.4.1 监听事件
事件是指在浏览器中通过内置的处理器监视特定的条件或用户行为,例如鼠标单击浏览器窗口中的按钮。浏览器中内置了大量的事件处理器,当这些事件处理器被触发时,会执行一个绑定在该处理器上的函数,然后执行相应的内容。在Vue中可以使用v-on指令来完成事件函数的绑定。
index.html文件代码如下:
在浏览器中运行的效果如图2.25所示。
图2.25 单击事件
在上面的示例代码中,单击“增加”按钮,num属性的值会被加1,并渲染到页面中。
2.4.2 事件处理方法
在2.4.1节的示例代码中,我们直接对num属性进行了操作,但是在实际的项目开发中,不能这样对属性进行直接操作。因为很多事件处理的逻辑比较复杂,应该把操作数据的代码写到具体的函数中。
index.html文件代码如下:
在浏览器中运行的效果如图2.26所示。
图2.26 事件处理函数
注意 v-on可以使用“@”代替,例如<button@click="add">增加</button>。
在调用事件函数时,我们还可以为事件函数传入参数。
index.html文件代码如下:
在浏览器中运行的效果如图2.27所示。
处理<button>标签,我们还可以在其他DOM元素上添加事件,如果在事件函数中要获取该事件所绑定的元素对象,可以使用事件函数的默认参数$ event传参。
图2.27 事件处理函数的传参
index.html文件代码如下:
在浏览器中运行的效果如图2.28所示。
图2.28 获取事件对象
event对象代表事件的状态,例如触发事件所绑定的元素、当前键盘和鼠标按键的状态、鼠标当前所在的位置等。当一个事件被触发时,和当前这个事件相关的所有信息都会被保存到event对象中。
2.4.3 事件修饰符
在事件处理程序中调用event.preventDefault()或event.stopPropagation()是非常常见的需求。尽管我们可以在方法中轻松实现这点,但更好的方式是:方法只有纯粹的数据逻辑,而不是去处理DOM事件细节。
为了解决这个问题,Vue.js为v-on提供了事件修饰符。使用修饰符可以节省很多代码和时间,这样便可以把更多的精力专注于处理程序的业务逻辑。
v-on的修饰符是由点开头的指令后缀来表示的,在Vue中事件的修饰符主要有以下几个。
(1).stop:等同于JavaScript中的event.stopPropagation(),阻止事件冒泡。
(2).self:只有触发当前修饰的元素时才会执行事件函数,不受事件冒泡影响。
(3).capture:使用事件捕获模式,即内部元素触发的事件先在此处理,然后才交由内部元素进行处理。
(4).once:只会触发一次。
(5).prevent:等同于JavaScript中的event.preventDefault(),阻止默认事件发生。
(6).passive:执行默认行为。
1.stop修饰符
stop修饰符用来阻止事件冒泡,禁止事件继续向父级元素传播。例如,在评论区,当单击每条评论时,触发评论外层的<div>事件,当单击评论区内的用户头像时,可以查看用户的个人信息。如果按照事件冒泡机制,当单击评论区内的用户头像,即触发了头像上的时间,也会触发评论区<div>的事件。
index.html文件代码如下:
在浏览器中运行,单击头像会触发clickAvatar()事件函数,效果如图2.29所示。根据事件冒泡机制,还会触发clickComment()事件函数,效果如图2.30所示。
图2.29 触发clickAvatar事件函数
图2.30 触发clickComment事件函数
上面示例代码中,单击头像会弹出两次提示框,在实际开发中,我们不需要在单击头像后触发外层的事件,此时可以使用Vue内置的修饰符.stop快速实现阻止事件冒泡的发生。
index.html文件代码如下:
在浏览器中运行的效果如图2.31所示。
图2.31 使用stop修饰符的效果
2.self修饰符
self修饰符可以跳过冒泡和捕获事件,只有当时触发了当前被修饰元素本身时,才执行绑定的事件函数。self修饰符会监听事件是否直接作用在当前元素上。
index.html文件代码如下:
在浏览器中运行,当单击内层元素时,会触发内部绑定的clickInner()事件函数,效果如图2.32所示。根据事件冒泡机制,会依次触发中间层和外层的事件函数,由于在中间层添加了.self修饰符,所以会跳过中间层,直接触发外层的事件函数,效果如图2.33所示。
图2.32 触发内层元素的事件函数
图2.33 触发外层元素的事件函数
3.capture修饰符
capture修饰符可以为元素添加事件监听器时使用事件捕获模式,即内部元素触发的事件先在此处理,然后才交由内部元素进行处理。使用capture修饰符可以改变事件冒泡的执行顺序,先执行添加了该修饰符的元素。
在前面的示例代码中,我们可以为中间层添加capture修饰符。
index.html文件代码如下:
在浏览器中运行,单击内层元素,应该先执行内层元素的事件函数,但此处先执行了中间层的clickCenter()函数,效果如图2.34所示。当执行完中间层的函数后,才去执行内层元素的clickInner()函数,效果如图2.35所示。最后执行外层元素的事件函数,如图2.36所示。
图2.34 触发中间层元素的事件函数
图2.35 触发内层元素的事件函数
图2.36 触发外层元素的事件函数
4.once修饰符
有时我们需要对元素只执行一次操作,例如社交软件上的点赞操作,可以使用once修饰符来完成。
index.html文件代码如下:
在浏览器中运行的效果如图2.37所示。
图2.37 once修饰符作用效果
5.prevent修饰符
prevent修饰符用于阻止浏览器的默认行为,例如<a>标签,使用prevent修饰符后,当单击超链接元素时,不会执行跳转动作。
index.html文件代码如下:
在浏览器中运行的效果如图2.38所示。
图2.38 prevent修饰符效果
6.passive修饰符
passive修饰符可以执行默认行为,元素本身的默认行为可以直接执行,为什么还要再添加一个passive修饰符来执行默认行为呢?这是因为浏览器只有当内核线程执行到事件监听器对应的JavaScript代码时,才能知道内部是否调用preventDefault()函数来阻止事件的默认行为,所以浏览器本身是没有办法对这种场景进行优化的。这种场景下,用户的手势事件无法快速产生,这会导致页面无法快速执行滑动逻辑,从而让用户感觉到页面卡顿。
简单来说,当每次触发元素上的事件时,浏览器都会去查询一下是否有preventDefault()函数阻止该次事件的默认行为。为元素添加passive修饰符,就是告诉浏览器不用再去检查了,表示该元素没有使用preventDefault()函数阻止默认行为。
不要把.passive和.prevent一起使用,因为.prevent将会被忽略,同时浏览器可能会向你展示一个警告。需要记住,.passive会告诉浏览器你不想阻止事件的默认行为。
注意 在使用修饰符时,修饰符的顺序很重要,相应的代码会以同样的顺序产生。因 此,用v-on:click.prevent.self会阻止所有的单击,而v-on:click.self.prevent只会阻止对元素自身的单击。
2.4.4 按键修饰符
在Vue中支持3种键盘事件的监听。
(1)keydown:键盘按键按下时触发。
(2)keyup:键盘按键抬起时触发。
(3)keypress:键盘按键按下与抬起间隔期间触发。
在日常的开发中,我们经常需要用到键盘操作,例如,在搜索一个关键词之后,用户会习惯性地按下Enter键,以便于搜索结果。在传统的网页设计工作中,通常需要使用JavaScript中事件对象的keyCode属性来判断用户到底按下的是哪个键,然后进行后续操作。
为了方便开发,Vue提供了在v-on监听事件时添加按键修饰符的方式,来监听用户的按键操作。
index.html文件代码如下:
在浏览器中运行的效果如图2.39所示。
图2.39 输入触发事件
在上面的示例代码中,为<input>输入框的keydown事件添加了.a修饰符,表示当前的输入框在输入字母a时会被监听,并触发绑定的事件函数。当在<input>输入框中输入字母a时会触发事件函数,但当输入字母b时不会触发事件函数。
为了方便使用按键修饰符,Vue提供了绝大多数常用的按键码的别名,具体别名如下。
· .enter
· .tab
· .delete(捕获“删除”和“退格”键)
· .esc
· .space
· .up
· .down
· .left
· .right
对于上面的案例可以使用按键修饰符的别名,代码如下:
2.4.5 系统修饰键
可以使用下面的修饰符实现仅在按下相应按键时才触发鼠标或键盘事件的监听器。
· .ctrl
· .alt
· .shift
· .meta
index.html文件代码如下:
在浏览器中运行的效果如图2.40所示。
图2.40 系统修饰键
2.4.6 为什么在HTML中监听事件
在前面各节的示例代码中,所有的事件都是通过在HTML标签中添加v-on属性实现的,这种事件监听的方式违背了关注点分离(Separation of Concern)这个长期以来的开发规范。之所以这样编写代码,是因为所有的Vue.js事件处理方法和表达式都严格绑定在当前视图的ViewModel上,它不会导致任何维护上的困难。实际上,使用v-on有以下几个特点。
(1)便于在HTML模板中快速定位JavaScript对应的事件函数。
(2)无须编写大量的JavaScript绑定事件的代码,可以更专注于编写业务逻辑。
(3)实现和DOM完全解耦,便于代码测试。
(4)当一个ViewModel被销毁时,所有的事件处理器都会被自动删除。