首页 > 技术文章 > 浏览器重排(回流)重绘以及优化方案

vigourice 2021-04-27 19:50 原文

一、什么是重排和重绘
要说清重排(reflow)和重绘(repaint),首先要理解排列和绘制,浏览器渲染页面时,在获取完html、css资源之后,会大致经过以下步骤。
(1) html生成html树
(2) css形成css规则
(3) 两者形成一个渲染树
(4) 去文档当中找寻各自的布局位置----- 排列
(5) 将内容填充到文档上 ----- 绘制
【排列】就是计算位置调整布局的过程,而【绘制】就是把它画上去的过程。【重排】也就是除了最开始排列的布局,后续经过一些操作而使得dom元素重新找寻位置的过程,【重绘】就是重新绘制内容的过程。

二、什么情况会引发重排和重绘
1、先来说重排,重排和位置的移动布局的变化有关,主要有以下几种情况会引发重排
(1) 浏览器的窗口发生变化,每放大、缩小一次浏览器的窗口,该页面的所有元素都要进行重排重绘
(2) 增加、删除、移动dom元素,更改dom元素的宽高内外边距、内容,修改dom元素的样式
(3) 进行dom元素宽高等属性的查询,因为每查询一次,浏览器都会对所有的元素进行重新计算,以确保计算的值是正确的

2、再来说重绘,重绘主要是元素的外观发生变化,不会重新布局,有以下情况会引发重绘
元素的背景(background)、文字颜色(color)、边框样式(outline)发生变化

3、重排和重绘的关系
重绘不一定需要重排,因为可能只是元素修改文字颜色,不需要重新布局,重排大多数情况都需要重绘,因为重新排列元素之后要绘制到屏幕上。

以下显示重排和重绘的耗时,方块两秒之后右移了100px

三、为什么要优化

因为重排和重绘不只是对单个的dom元素进行操作,而是对整个【图层】进行操作,需要花费时间,如果频率高,非常的影响性能。

那什么是图层?如下图所示


因为每一次的重排重绘都是操作整个图层,那么我们可以将需要频繁操作的dom元素另外建立一个图层,这样可以尽可能少的触发重排重绘。

那么什么情况可以开启图层?
针对频繁操作的行为,chrome浏览器自动开启了图层,主要有以下几种情况
1、css 3D变化的图形 ---- transform: translateX(0)
2、html5中的<video>标签
3、canvas绘图中的节点
4、css 动画的节点 --- keyframes animation
5、拥有css加速属性 --- will-change: transform

除此之外,还可以使用以下方式进行优化
1、对元素进行移动时,使用transform替代对元素top、left、right的操作,因为css3的整个操作是对图层的组合来实现的,所以不会引发重绘重排。

#node{
  // position: relative; 
  transform: translateX(0);
  width: 100px;
  height: 100px;
  background: pink;
}
var node = document.querySelector("#node");
setTimeout(function(){
    //   node.style.left="100px";
   node.style.transform="translateX(100px)";
},2000);

同样是对一个元素两秒后移动100px,可以看到对比下图对比

2、将多次对样式的操作合并成一次
不要一次一次的修改样式,而是预先定义好class,直接修改DOM的className,这样只会引发一次重排重绘
3、将dom离线后修改
如果要对dom元素进行多次操作,首先将dom设置为不可见,然后再对dom操作,操作完成后再将dom元素设置为可见,这样只会有两次重排重绘
4、利用文档碎片 documentFragment
documentFragment 不是真实 dom树的一部分,它的变化不会触发dom树的重新渲染,且不会导致性能等问题,将创建的新元素全部添加到documentFragment上,最后让documentFragment一起插入到dom元素中

const list = ['哈尔的移动城堡', '千与千寻的神隐', '起风了']
const ul = document.getElementsByTagName("ul")[0]
let fragment = document.createDocumentFragment()
for(let i in list){
    fragment.appendChild(list[i])
}
ul.appendChild(fragment)

 

推荐阅读