使用 passive 改善的滚屏性能
共 2021字,需浏览 5分钟
·
2024-06-09 10:18
EventTarget.addEventListener() 方法将指定的监听器注册到 EventTarget 上,当该对象触发指定的事件时,指定的回调函数就会被执行。
语法
addEventListener(type, listener);
addEventListener(type, listener, options);
addEventListener(type, listener, useCapture);
参数
type
表示监听事件类型的大小写敏感的字符串。
listener
当所监听的事件类型触发时,会接收到一个事件通知(实现了 Event 接口的对象)对象。listener 必须是一个实现了 EventListener 接口的对象,或者是一个函数。有关回调本身的详细信息,请参阅事件监听回调
options 可选
一个指定有关 listener 属性的可选参数对象。可用的选项如下:
capture 可选:一个布尔值,表示 listener 会在该类型的事件捕获阶段传播到该 EventTarget 时触发。once 可选:一个布尔值,表示 listener 在添加之后最多只调用一次。如果为 true,listener 会在其被调用之后自动移除。
passive 可选:一个布尔值,设置为 true 时,表示 listener 永远不会调用 preventDefault()。如果 listener 仍然调用了这个函数,客户端将会忽略它并抛出一个控制台警告。查看使用 passive 改善滚屏性能以了解更多。
signal 可选:AbortSignal,该 AbortSignal 的 abort() 方法被调用时,监听器会被移除。
useCapture 可选
一个布尔值,表示在 DOM 树中注册了 listener 的元素,是否要先于它下面的 EventTarget 调用该 listener。
使用 passive 改善的滚屏性能
根据规范,passive 选项的默认值始终为false。但是,这引入了处理某些触摸事件(以及其他)的事件监听器在尝试处理滚动时阻止浏览器的主线程的可能性,从而导致滚动处理期间性能可能大大降低。
为防止出现此问题,某些浏览器(特别是Chrome和Firefox)已将touchstart和touchmove事件的passive选项的默认值更改为true文档级节点 Window,Document和Document.body。这可以防止调用事件监听器,因此在用户滚动时无法阻止页面呈现。
const elem = document.getElementById('elem');
elem.addEventListener('touchmove', function listener() { /* do something */ }, { passive: true });
添加passive参数后,touchmove事件不会阻塞页面的滚动(同样适用于鼠标的滚轮事件)。
很多移动端的页面都会监听 touchstart 等 touch 事件,像这样:
document.addEventListener("touchstart", function(e){
... // 浏览器不知道这里会不会有 e.preventDefault()
})
由于 touchstart 事件对象的 cancelable 属性为 true,也就是说它的默认行为可以被监听器通过 preventDefault() 方法阻止,那它的默认行为是什么呢,通常来说就是滚动当前页面(还可能是缩放页面),如果它的默认行为被阻止了,页面就必须静止不动。但浏览器无法预先知道一个监听器会不会调用 preventDefault(),它能做的只有等监听器执行完后再去执行默认行为,而监听器执行是要耗时的,有些甚至耗时很明显,这样就会导致页面卡顿。即便监听器是个空函数,也会产生一定的卡顿,毕竟空函数的执行也会耗时。
有 80% 的滚动事件监听器是不会阻止默认行为的,也就是说大部分情况下,浏览器是白等了。所以,passive 监听器诞生了,passive 的意思是“顺从的”,表示它不会对事件的默认行为说 no,浏览器知道了一个监听器是 passive 的,它就可以在两个线程里同时执行监听器中的 JavaScript 代码和浏览器的默认行为了。