重绘和回流会在我们设置节点样式时频繁出现,同时也会很大程度上影响性能。
1、重绘是当节点需要更改外观而不会影响布局的,比如改变 color 就叫称为重绘。
2、回流是布局或者几何属性需要改变就称为回流。
回流必定会发生重绘,重绘不一定会引发回流。回流所需的成本比重绘高的多,改变父节点里的子节点很可能会导致父节点的一系列回流。
以下几个动作可能会导致性能问题:
1、改变window大小
2、改变字体
3、添加或删除样式
4、文字改变
5、定位或浮动
6、盒模型
并且很多人不知道的是,重绘和回流其实也和Eventloop有关
1、当Eventloop执行完Microtasks后,会判断document是否需要更新,因为浏览器时60Hz的刷新率,每16.6ms才会更新一次。
2、然后判断是否有 resize 或者 scroll 事件,有的话会去触发事件,所以 resize 和 scroll 事件也是至少 16ms 才会触发一次,并且自带节流功能。
3、判断是否触发了 media query。
4、更新动画并且发送事件。
5、判断是否有全屏操作事件。
6、执行 requestAnimationFrame 回调。
7、执行 IntersectionObserver 回调,该方法用于判断元素是否可见,可以用于懒加载上,但是兼容性不好。
8、更新界面。
9、以上就是一帧中可能会做的事情。如果在一帧中有空闲时间,就会去执行 requestIdleCallback 回调。
既然我们已经知道了重绘和回流会影响性能,接下来介绍如何减少重绘和回流的次数。
1、使用transfrom替代top。
<div class="test"></div> <style> .test { position: absolute; top: 10px; width: 100px; height: 100px; background: red; } </style> <script> setTimeout(() => { // 引起回流 document.querySelector('.test').style.top = '100px' }, 1000) </script>
2、使用 visibility 替换 display: none ,因为前者只会引起重绘,后者会引发回流(改变了布局)。
3、不要把节点的属性值放在一个循环里当成循环里的变量。
for(let i = 0; i < 1000; i++) { // 获取 offsetTop 会导致回流,因为需要去获取正确的值 console.log(document.querySelector('.test').style.offsetTop) }
4、不要使用 table 布局,可能很小的一个小改动会造成整个 table 的重新布局。
5、动画实现的速度的选择,动画速度越快,回流次数越多,也可以选择使用 requestAnimationFrame。
6、CSS 选择符从右往左匹配查找,避免节点层级过多。
7、将频繁重绘或者回流的节点设置为图层,图层能够阻止该节点的渲染行为影响别的节点。比如对于 video 标签来说,浏览器会自动将该节点变为图层。
8、设置节点为图层的方式有很多,我们可以通过以下几个常用属性可以生成新图层
will-change
video、iframe 标签
从文件大小考虑
从 script 标签使用上来考虑
从 CSS、HTML 的代码书写上来考虑
从需要下载的内容是否需要在首屏使用上来考虑
我们了解了浏览器如何将文件渲染为页面,同时也掌握了一些优化的小技巧。