首页 > 编程笔记

Vue事件修饰符详解

Vue 可以对事件添加一些通用的限制,例如添加阻止事件冒泡,Vue 对这种事件的限制提供了特定的写法,称之为修饰符。

Vue 事件修饰符的语法如下:
v-on:事件.修饰符
在事件处理程序中,调用 event.preventDefault()(阻止默认行为)或 event.stopPropagation()(阻止事件冒泡)是非常常见的需求。尽管可以在方法中轻松实现这一点,但更好的方式是使用纯粹的数据逻辑,而不是去处理 DOM 事件细节。

Vue 事件修饰符处理了许多 DOM 事件的细节,让我们不再需要花费大量的时间去处理这些烦恼的事情,而将更多的精力用于程序的逻辑处理。

在 Vue 中,事件修饰符主要有:
下面分别来看一下每个修饰符的用法。

stop事件修饰符

stop 修饰符用来阻止事件冒泡。

在下面的示例中,创建一个 div 元素,在其内部也创建一个 div 元素,并分别为它们添加单击事件。根据事件的冒泡机制可以得知,当单击内部的 div 元素之后,会扩散到父元素 div,从而触发父元素的单击事件。
<head>
    <meta charset="UTF-8">
    <title>冒泡事件</title>
<style>
    .outside{
        width: 200px;
        height: 100px;
        border: 1px solid red;
        text-align: center;
    }
    .inside{
        width: 100px;
        height: 50px;
        border:1px solid black;
        margin:15% 25%;
    }
</style>
</head>
<body>
<div id="app">
    <div class="outside" @click="outside">
        <div class="inside" @click ="inside">冒泡事件</div>
    </div>
</div>
<!--引入Vue文件-->
<script src="https://unpkg.com/vue@next"></script>
<script>
    //创建一个应用程序实例
    const vm= Vue.createApp({
        methods: {
            outside: function () {
                alert("外面div的单击事件")
            },
            inside: function () {
                alert("内部div的单击事件")
            }
        }
    //在指定的DOM元素上装载应用程序实例的根组件
    }).mount('#app');
</script>
在 Chrome 浏览器中运行程序,单击内部(inside)元素,触发自身事件,效果如下图所示。


图 1 触发内部元素事件

根据事件的冒泡机制,也会触发外部(outside)元素,效果如下图所示。


图 2 触发外部元素事件

使用 stop 修饰符阻止事件冒泡。修改上面 HTML 对应的代码:
<div id="app">
    <div class="outside" @click="outside">
        <div class="inside" @click.stop="inside">阻止事件冒泡</div>
    </div>
</div>
在 Chrome 浏览器中运行程序,单击内部的 div 后,将不再触发父元素单击事件,如下图所示。


图 3 只触发内部元素事件

capture事件修饰符

事件捕获模式与事件冒泡模式是一对相反的事件处理流程,当想要将页面元素的事件流改为事件捕获模式时,只需要在父级元素的事件上使用 capture 修饰符即可。若有多个该修饰符,则由外而内触发。

在下面的示例中,创建了 3 个 div 元素,把它们分别嵌套,并添加单击事件。为外层的两个 div 元素添加 capture 修饰符,当单击内部的 div 元素时,将从外向内触发含有 capture 修饰符的 div 元素的事件。
<style>
    .outside{
        width: 300px;
        height: 180px;
        color:white;
        font-size: 30px;
        background: red;
        margin-top: 120px;
    }
    .center{
        width: 200px;
        height: 120px;
        background: #17a2b8;
    }
    .inside{
        width: 100px;
        height: 60px;
        background: #a9b4ba;
    }
</style>
<div id="app">
    <div class="outside" @click.capture="outside">
        <div class="center" @click.capture="center">
            <div class="inside" @click="inside">内部</div>
            中间
        </div>
        外层
    </div>
</div>
<!--引入Vue文件-->
<script src="https://unpkg.com/vue@next"></script>
<script>
    //创建一个应用程序实例
    const vm= Vue.createApp({
        methods: {
            outside:function(){
                alert("外面的div")
            },
            center:function(){
                alert("中间的div")
            },
            inside: function () {
                alert("内部的div")
            }
        }
    //在指定的DOM元素上装载应用程序实例的根组件
    }).mount('#app');
</script>
在 Chrome 浏览器中运行程序,单击内部的 div 元素,会先触发添加了 capture 修饰符的外层 div 元素,然后触发中间 div 元素,最后触发单击的内部元素。

self事件修饰符

self 修饰符可以理解为跳过冒泡事件和捕获事件,只有直接作用在该元素上的事件才可以执行。

self 修饰符会监视事件是否直接作用在元素上,若不是,则冒泡跳过该元素。

例如:
<style>
    .outside{
        width: 300px;
        height: 180px;
        color:white;
        font-size: 30px;
        background: red;
        margin-top: 100px;
    }
    .center{
        width: 200px;
        height: 120px;
        background: #17a2b8;
    }
    .inside{
        width: 100px;
        height: 60px;
        background: #a9b4ba;
    }
</style>
<div id="app">
    <div class="outside" @click="outside">
        <div class="center" @click.self="center">
            <div class="inside" @click="inside">内部</div>
            中间
        </div>
        外层
    </div>
</div>
<!--引入Vue文件-->
<script src="https://unpkg.com/vue@next"></script>
<script>
    //创建一个应用程序实例
    const vm= Vue.createApp({
        methods: {
            outside: function () {
                alert("外面的div")
            },
            center: function () {
                alert("中间的div")
            },
            inside: function () {
                alert("内部的div")
            }
        }
    //在指定的DOM元素上装载应用程序实例的根组件
    }).mount('#app');
</script>
在 Chrome 浏览器中运行程序,单击内部的 div 后,触发该元素的单击事件。由于中间 div 添加了 self 修饰符,因此直接单击该元素就会跳过,内部 div 执行完毕后,外层的 div 紧接着执行。

once事件修饰符

有时需要只执行一次的操作,比如微信朋友圈点赞,便可以使用 once 修饰符来完成。

不像其他只能对原生的 DOM 事件起作用的修饰符,once 修饰符还能被用到自定义的组件事件上。

例如:
<div id="app">
    <button @click.once="add">点赞 {{num }}</button>
</div>
<!--引入Vue文件-->
<script src="https://unpkg.com/vue@next"></script>
<script>
    //创建一个应用程序实例
    const vm= Vue.createApp({
        //该函数返回数据对象
        data(){
          return{
             num:0
           }
        },
        methods:{
            add:function(){
                this.num +=1
            },
        }
    //在指定的DOM元素上装载应用程序实例的根组件
    }).mount('#app');
</script>
在 Chrome 浏览器中运行程序,单击“点赞0”按钮,count 值从 0 变成 1,之后,无论再单击多少次,count 的值仍然是 1,效果如下图所示。


图 4 once修饰符作用效果

prevent事件修饰符

prevent 修饰符用于阻止默认行为,例如 <a> 标签,当单击标签时,默认行为会跳转到对应的链接,如果在 <a> 标签上添加 prevent 修饰符,则不会跳转到对应的链接。

注意,不要把 passive 和 prevent 修饰符一起使用,因为 prevent 将会被忽略,同时浏览器可能会展示一个警告。passive 修饰符会告诉浏览器不想阻止事件的默认行为。

例如:
<div id="app">
    <div style="margin-top: 100px">
        <a @click.prevent="alert()" href="https://cn.vuejs.org" >阻止跳转</a>
    </div>
</div>
<!--引入Vue文件-->
<script src="https://unpkg.com/vue@next"></script>
<script>
    //创建一个应用程序实例
    const vm= Vue.createApp({
        methods:{
            alert:function(){
                alert("阻止<a>标签的链接")
            }
        }
     //在指定的DOM元素上装载应用程序实例的根组件
    }).mount('#app');
</script>
在 Chrome 浏览器中运行程序,单击“阻止跳转”链接,触发 alert() 事件弹出“阻止 <a> 标签的链接”,效果如下图所示;然后单击“确定”按钮,可以发现页面将不再进行跳转。


图 5 prevent修饰符

passive事件修饰符

明明是默认执行的行为,为什么还要使用 passive 修饰符呢?原因是浏览器只有等内核线程执行到事件监听器对应的 JavaScript 代码时,才能知道内部是否会调用 preventDefault 函数来阻止事件的默认行为,所以浏览器本身是没有办法对这种场景进行优化的。在这种场景下,用户的手势事件无法快速产生,会导致页面无法快速执行滑动逻辑,从而让用户感觉到页面卡顿。

通俗地说,就是每次事件产生时,浏览器都会去查询一下是否有 preventDefault 阻止该次事件的默认动作,加上 passive 修饰符就是为了告诉浏览器,不用查询了,没有 preventDefault 阻止默认行为。

passive 修饰符一般用在滚动监听、@scoll 和 @touchmove 中。因为滚动监听过程中,移动每个像素都会产生一次事件,每次都使用内核线程查询 prevent 会使滑动卡顿,通过 passive 修饰符将内核线程查询跳过,可以大大提升滑动的流畅度。

注意,使用修饰符时,顺序很重要,相应的代码会以同样的顺序产生。因此,使用 v-on:click.prevent.self 会阻止所有的单击,而 v-on:click.self.prevent 只会阻止对元素自身的单击。

推荐阅读