我是卡喵妹,你可以叫我卡妹,你也可以叫我喵妹,或者你叫我小菜喵也可以。(杀疯了杀疯了)
前言
我第一次看 Vue 的官方文档已经是几年前了,第一次真的是认真研读了一次,后来在学习工作中遇到忘记的地方会翻到对应的地方扫几眼,再也没有完整且认真地研读过了。
最近不忘初心重学 Vue,越看越有味道越看越上头停不下来了,还是发现了几个有点意思的地方,搜索搜集了感兴趣的内容在此记录?。
一、DOM 事件流
想必看过《红宝书》的前端 er 对标题都不会特别陌生,我们来看看定义:当一个 HTML 元素产生一个事件时,该事件会在树形结构的 DOM 上面沿着元素节点路径进行传播,事件所经过的路径结点都会收到该事件,这个传播过程可称为 DOM 事件流。

二、DOM 事件流模型
DOM 事件流分为 捕获型事件流 和 冒泡型事件流。两种事件流分别对应三阶段 DOM 事件流模型中的捕获阶段和冒泡阶段:
- 捕获阶段:事件从最外面的祖先节点依次传递到最里面的后代节点
- 目标阶段:真正的目标节点正在处理事件的阶段
- 冒泡阶段:事件又从最里面的后代节点逐层传出到最外面的祖先节点

三、Vue 之事件处理
Vue 中提供了事件绑定的语法糖,我们可以很简单地在标签中直接使用 @click="handleClick($event)" 就可以绑定点击事件。
而且在 Vue 里面,Vue 的事件触发默认为冒泡过程监听,意思就是上面 @click="handleClick($event)" 中点击事件的执行是冒泡过程触发的。
四、Vue 之事件修饰符
4.1 .capture
捕获监听器
<div @click="log(1)" @click.capture="log(1)" style="background-color: #00f">
<div @click="log(2)" @click.capture="log(2)" style="background-color: #66f">
<div @click="log(3)" @click.capture="log(3)" style="background-color: #ccf">
<div @click="log(4)" @click.capture="log(4)" style="background-color: #fff">
点击这里
</div>
</div>
</div>
</div>
复制代码
控制台打印结果为:1 2 3 4 4 3 2 1

符合 DOM 事件流模型,先捕获后冒泡,.capture 的作用就是在捕获阶段触发事件。
4.2 .stop
阻止事件传递
<div @click="log(1)" @click.capture="log(1)" style="background-color: #00f">
<div @click="log(2)" @click.capture="log(2)" style="background-color: #66f">
<div @click="log(3)" @click.capture.stop="log(3)" style="background-color: #ccf">
<div @click="log(4)" @click.capture="log(4)" style="background-color: #fff">
点击这里
</div>
</div>
</div>
</div>
复制代码
控制台打印结果为:1 2 3
说明 .stop 真正的作用是阻止事件的传递,不仅阻止捕获事件流,也会阻止冒泡事件流。
.stop 最常见的应用场景:比如移动端购物车的商品列表,点击商品列表跳转商品详情,商品列表右下角有个删除商品的按钮,点击按钮删除该商品。假设我们正常监听点击事件,当我们点击删除按钮时,触发删除事件,但是由于事件冒泡,稍后触发跳转商品详情的事件。结论就是:在删除按钮监听事件增加 .stop 事件修饰符,避免事件传递造成非预期的结果。
4.3 .prevent
阻止默认事件的触发:比如某些 HTML 标签拥有自身的默认事件,如
a[href=""]、button[type="submit"]标签在冒泡结束后会开始执行默认事件。注意默认事件虽然是冒泡后开始,但不会因为.stop事件修饰符阻止事件传递而停止。
<div @click="log(1)" @click.capture="log(1)" style="background-color: #00f">
<div @click="log(2)" @click.capture="log(2)" style="background-color: #66f">
<div @click="log(3)" @click.capture.stop="log(3)" style="background-color: #ccf">
<a @click="log(4)" @click.capture="log(4)" href="javascript: console.log('x')"
style="background-color: #fff">
点击这里
</a>
</div>
</div>
</div>
复制代码
控制台打印结果为:1 2 3 x
上例中说明了 .stop 事件修饰符无法阻止 a[href=""] 中默认事件的触发。
<!-- 将 .prevent 绑在冒泡阶段 -->
<div @click="log(1)" @click.capture="log(1)" style="background-color: #00f">
<div @click="log(2)" @click.capture="log(2)" style="background-color: #66f">
<div @click="log(3)" @click.capture="log(3)" style="background-color: #ccf">
<a @click.prevent="log(4)" @click.capture="log(4)" href="javascript: console.log('x')"
style="background-color: #fff">
点击这里
</a>
</div>
</div>
</div>
<!-- 将 .prevent 绑在捕获阶段 -->
<div @click="log(1)" @click.capture="log(1)" style="background-color: #00f">
<div @click="log(2)" @click.capture="log(2)" style="background-color: #66f">
<div @click="log(3)" @click.capture="log(3)" style="background-color: #ccf">
<a @click="log(4)" @click.capture.prevent="log(4)" href="javascript: console.log('x')"
style="background-color: #fff">
点击这里
</a>
</div>
</div>
</div>
复制代码
控制台打印结果为:1 2 3 4 4 3 2 1
上述两例证明,无论是在冒泡阶段阻止默认事件还是在捕获阶段阻止默认事件,结果都是一样的。
4.4 .passive
不阻止默认事件的触发:浏览器只有等内核线程执行到事件监听器对应的 JavaScript 代码时,才能知道内部是否会调用 preventDefault 函数来阻止事件的默认行为,所以浏览器本身是没有办法对这种场景进行优化的。这种场景下,用户的手势事件无法快速产生,会导致页面无法快速执行滑动逻辑,从而让用户感觉到页面卡顿。(通俗点说就是每次事件产生,浏览器都会去查询一下是否有 preventDefault 阻止该次事件的默认动作。我们加上 passive 就是为了告诉浏览器,不用查询了,我们没用 preventDefault 阻止默认动作。)
应用场景:一般用在滚动监听 @scoll、@touchmove,因为滚动监听过程中移动每个像素都会产生一次事件,每次都使用内核线程查询 prevent 会使滑动卡顿。我们通过 passive 将内核线程查询跳过,可以大大提升滑动的流畅度。
注:passive 和 prevent 冲突,不能同时绑定在一个监听器上。
4.5 .self
只有点击元素本身的时候才会触发这个元素的事件。
<div @click="log(1)" @click.capture="log(1)" style="background-color: #00f">
<div @click="log(2)" @click.capture.self="log(2)" style="background-color: #66f">
<div @click.self="log(3)" @click.capture="log(3)" style="background-color: #ccf">
<a @click="log(4)" @click.capture="log(4)" href="javascript: console.log('x')"
style="background-color: #fff">
点击这里
</a>
</div>
</div>
</div>
复制代码
- 点击 a 标签控制台打印结果为:
1 3 4 4 2 1 x - 点击 3 图层控制台打印结果为:
1 3 3 2 1
<div @click="log(1)" @click.capture="log(1)" style="background-color: #00f">
<div @click="log(2)" @click.capture="log(2)" style="background-color: #66f">
<div @click="log(3)" @click.capture="log(3)" style="background-color: #ccf">
<a @click.prevent.self="log(4)" @click.capturet="log(4)" href="javascript: console.log('x')"
style="background-color: #fff">
点击这里
<div style="background-color: #ccc">5</div>
</a>
</div>
</div>
</div>
复制代码
- 点击 a 标签控制台打印结果为:
1 2 3 4 4 3 2 1 - 点击 5 图层控制台打印结果为:
1 3 4 3 2 1
<div @click="log(1)" @click.capture="log(1)" style="background-color: #00f">
<div @click="log(2)" @click.capture="log(2)" style="background-color: #66f">
<div @click="log(3)" @click.capture="log(3)" style="background-color: #ccf">
<a @click.self.prevent="log(4)" @click.capture="log(4)" href="javascript: console.log('x')"
style="background-color: #fff">
点击这里
<div style="background-color: #ccc">5</div>
</a>
</div>
</div>
</div>
复制代码
- 点击 a 标签控制台打印结果为:
1 2 3 4 4 3 2 1 - 点击 5 图层控制台打印结果为:
1 2 3 4 3 2 1 x(x 在最后是由于默认事件在冒泡结束之后执行)
注:self 写在 prevent 前时,prevent 会被 self 影响。直接点击这个目标时才会触发 prevent。因为 self 拦截住了监听,后面的 prevent 也一起失效了。
4.6 .native
原型绑定:只有使用 vue 组件才会用到该修饰符。
<el-input @click.native=""> 相当于把事件绑定在 input[class="el-input__inner"] 上。
4.7 .once
使得元素的事件只触发一次。
绑定 .once 的监听器只会触发一次,在第一次触发后该监听器会被移除。






















![[桜井宁宁]COS和泉纱雾超可爱写真福利集-一一网](https://www.proyy.com/skycj/data/images/2020-12-13/4d3cf227a85d7e79f5d6b4efb6bde3e8.jpg)

![[桜井宁宁] 爆乳奶牛少女cos写真-一一网](https://www.proyy.com/skycj/data/images/2020-12-13/d40483e126fcf567894e89c65eaca655.jpg)