之前看防抖
和节流
的时候,看到短时间内大量的事件触发会引起浏览器卡死,浪费浏览器性能,那么为什么事件触发会引起阻塞页面的情况呢?引起页面阻塞的原因真的是因为事件触发太多了吗?
注:本篇文章测试用例来源于 Chrome
怎样算卡?
页面运行阶段怎样算是卡
?如果以用户的角度来看,那点击一个按钮没响应,算卡;下拉页面没动,算卡;图片停止加载,GIF 图不转了,动画停止了,算卡;总之,就是页面无法做出用户认为正确的动作
就算卡,此时在本篇文章中认为是页面被阻塞,那么事件的高强度触发会不会导致页面的卡
呢?
交互失效
关于浏览器运行这块, JS 线程是和 GUI 渲染线程是互斥,我们通常说的页面卡死,就是不动了,没响应了,通常可能就是 JS 线程在高强度
占用中,而为什么说,事件高频触发会影响到浏览器卡死,是因为 Event Loop 机制,事件触发后会往任务队列中的塞入任务,就是像点击事件的回调这种,然后 JS 引擎会读取任务队列,然后执行 JS, 这个时候如果说你的 JS 执行时间很久,那么就会阻塞页面的渲染,那么高频触发的话就会一直阻塞页面,那么为了证明上面的猜想
(目前是),就去试试看
首先验证,点击按钮没响应这块,这方面对应的是 JS 无法及时执行,监听 mouseenter 事件(因为点击事件在动图不太能看出来),和以一个空转的 for
循环来模拟 JS 高强度运算的情况,代码如下
<!DOCTYPE html>
<html>
<body>
<div onmouseenter="add()"></div>
</body>
<script>
// 9 个 0
// 点击一次会打印一次
let num = 0;
function add() {
console.log(++num);
for (let i = 0; i < 3000000000; i++) {}
}
</script>
<style>
div {
width: 100px;
height: 100px;
background-color: black;
}
</style>
</html>
结果如下
mouseenter
事件触发了三次,就是鼠标移进去 div
3 次,第一次马上打印出来了,第二次等了一下才打印,这说明 JS 的执行的确会阻塞页面,但是,这是交互上的阻塞,导致 JS 延迟执行,因为 JS 线程
在第二次事件触发时还在执行第一次的 JS, 注意,有小细节,这里提到了 线程,其实刚才页面上还有一个线程,那就是事件触发线程
,从刚才一个打印了 3 次而不是 1 次或者两次,可以说明事件触发线程
它是一个独立的线程,类似下载线程,不是和 JS 线程互斥的,当我们 JS 线程在执行的时候,事件也能够正常的被触发
这里也回一下主题,就是事件高频触发会不会阻塞页面,从这种情况来看,因为事件触发线程
的独立,所以 JS 线程会不断执行被任务队列中的任务,如果恰巧这个任务是计算复杂型的 JS, 要花很长时间,比如点击一个按钮后没响应再多次点击,就会造成 JS 延迟执行,最终导致整个页面的交互失效
这种情况可以考虑使用 web worker 再开一个线程的方式去处理,感兴趣的去百度
动画类阻塞
JS 延迟执行算是交互类型的一种阻塞,那么 JS 线程是和 GUI 线程互斥, 事件的高频率触发,也必然会不断执行 JS, 那么类似 GIF 和 动画这种涉及 GUI 线程的会不会被阻塞,也就是证明 JS 线程和 GUI 线程互斥,还是以上面的例子为模板,加上一个 img 动图
<!-- ... -->
<div onmouseenter="add()"></div>
<p>---分割线---</p>
<img src="http://localhost:8000/gif.gif" />
<!-- ... -->
结果如下
这个结果我还是有点意外的,因为我之前看过一篇文章曾经说过 JS 执行会阻塞动图的加载,现在实践看来并不是真的,难道是我们用的浏览器不一样?至少 Chrome 的计算复杂型
的 JS 并不会阻塞动图的加载
那么排除掉事件触发对动图加载的影响,接下来将看一下 CSS 动画会不会受到 JS 的影响,还是以上面的例子为模板,加上一个从左到右的动画
<!-- ... -->
<div onmouseenter="add()"></div>
<p>---分割线---</p>
<div class="animation"></div>
<!-- ... -->
<style>
/* ... */
@keyframes move {
from {
left: 0;
}
to {
left: 100%;
}
}
.animation {
position: absolute;
animation: move 5s linear infinite;
}
</style>
还是熟悉的 3 次 mouseenter
事件,我们设计的动画最终会等待 JS 的执行完成才能够继续进行,因此我们可以得出结论,在页面存在 CSS 动画的情况下,如果事件的高频触发并且触发的回调是计算复杂型
的 JS, 会导致页面的阻塞(动画无法进行)
总结
- 事件的频繁触发并不是导致页面阻塞的真正原因,因为事件触发线程是独立线程
- 事件触发的回调的 JS 是否是
计算复杂型
,会不会是一个长任务才是阻塞页面的真正原因