addEventListener中设置passive的作用

2,945 阅读2分钟

问题

看到vue中有事件修饰符中的.passive,看了vue官方文档的解释只知道它可以提示性能,不是太清楚她的作用,深入学习一下passive的用途及原理。

这个 .passive 修饰符尤其能够提升移动端的性能。

passive

.passive这个修饰符其实出自addEventListener中options里的passive属性。

target.addEventListener(type, listener, options | useCapture);

options 可选
一个指定有关 listener 属性的可选参数对象。可用的选项如下:

capture: Boolean,表示 listener 会在该类型的事件捕获阶段传播到该 EventTarget 时触发。
once: Boolean,表示 listener 在添加之后最多只调用一次。如果是 true, listener 会在其被调用之后自动移除。
passive: Boolean,设置为true时,表示 listener 永远不会调用 preventDefault()。如果 listener 仍然调用了这个函数,客户端将会忽略它并抛出一个控制台警告。查看 使用 passive 改善的滚屏性能 了解更多.
signal:AbortSignal,该 AbortSignal 的 abort() 方法被调用时,监听器会被移除。

passive:true如何使得性能提升

浏览器内核渲染页面时有2个线程,一个主线程,负责js执行,另一个绘制线程,负责绘制画面。当事件触发时,主线程负责执行事件回调函数,完事后告诉绘制线程可以绘制画面了,但是如果主线程一直很忙,事件回调函数就迟迟不能执行,绘制画面也就卡住了,用户就感知到了卡顿。

为什么绘制线程要等待主线程 ?

事件回调函数中可能存在event.preventDefault()语句,这个语句的执行是会影响绘制线程的执行的,所以要等待回调执行结束,才能知道回调函数中是不是有event.preventDefault()

在浏览器中,有些事件可以不经过内核线程就能快速处理,如手势操作事件(滑动、捏合),手势操作事件是由用户连续的普通操作事件组合产生,如连续的mousewheel/touchmove事件可能会生成GestureScrollBegin/GestureScrollUpdate等手势事件。手势操作事件可以直接在已经渲染好的内容快照上操作,如滑动手势事件,直接对页面已经渲染好的内容快照进行滑动展示即可。这类事件需要及时响应,其实不需要等待事件回调函数执行完。

这时就用到passive属性了,passive如果设置为true,则绘制线程不用等待主线程了的事件回调函数执行完,它们可以并行执行,这样绘制线程也可立即响应,没有卡顿感,如果回调函数中有调用event.preventDefault(),则不再起作用,执行到这句时,浏览器会给出警告。

参考

MDN addEventLister

让页面滑动流畅得飞起的新特性:Passive Event Listeners