我们先来看一个Demo, 在 Demo 中,在touchStart
的事件的callback
中跑了一段JS
的循环,打开Chrome
的devtools
,切换到移动端的模式,在页面上滑动的时候,可以明显感觉到左边盒子的滚动有明显的延迟和卡顿,而右边的盒子滚动起来却很顺滑;这两个盒子的touchStart
事件的callback
跑的循环是一样的,差异在于右边盒子将passive
置为了true
。
页面的平滑滚动对于用户体验来说是很重要的,特别是移动端,如果开发人员在touchstart
或者touchmove
中执行了涉及JS
的代码,会影响页面的滚动,原因在于页面的滚动在浏览器中是由另一个线程负责的,在页面滚动的过程中,如果触发了JS
的执行,这个线程会等待主线程执行完JS
再触发滚动(因为在事件回调中可能执行preventDefault
,会将滚动行为停止掉),所以,类似于上面demo
的代码,左边盒子的touchstart
的callback
执行了耗时的JS
计算,页面的滚动在等待JS
的执行完成,用户体验上就会感到卡顿。
const test = document.getElementById('test')
const test1 = document.getElementById('test1')
test.addEventListener('touchstart', e => {
for (let i = 0; i < 10000000; i += 1) {
for (let j = 0; j < 100; j += 1) {}
}
})
test1.addEventListener(
'touchstart',
e => {
let sum = 0
for (let i = 0; i < 10000000; i += 1) {
for (let j = 0; j < 100; j += 1) {}
}
},
{ passive: true }
)
在 DOM 的事件绑定中,我们使用addEventListenr
这个函数签名的第二个参数是一个obj
,我们常用到的就是{capture: true}
,用于在事件的捕获阶段(从文档的顶层到触发的 dom)绑定事件处理函数,这里还存在一个参数{passive: true}
,将这个参数置为true
之后,相当于通知浏览器,我这个回调函数不会执行preventDefault
,对于上面我们demo
中看到的滚动卡顿的问题来说,就相当于告诉控制滚动的线程,你继续滚动,我不会打断正常的滚动,所以就产生了demo
中的差异。
兴趣遍地都是,坚持和持之以恒才是稀缺的