一、Reflow和Repaint是什么?
首先,我们需要了解一下浏览器渲染的大致流程:
- 解析HTML文档,构建DOM树
- 解析CSS属性,构建CSSOM树
- 结合DOM树和CSSOM树,构建render树
- 在render树的基础上进行布局,计算每个节点的几何结构
- 把每个节点绘制在屏幕上
一个页面可以简单的看成两个部分:
- DOM节点:描述页面的结构
- DOM节点的属性:描述DOM节点如何呈现
不难看出Reflow发生在在render树的基础上进行布局,而Repaint发生在把每个节点绘制在屏幕上这一步。
Reflow(重排)
当涉及到DOM节点的布局属性发生变化时,就会重新计算该属性,浏览器会重新描绘相应的元素,此过程叫回流(Reflow)。
Repaint(重绘)
当影响到DOM元素可见性的属性发生变化(如color)时,浏览器会重新描绘相应的元素,此过程成为重绘(Repaint)。因此重排必然会引起重绘。
二、引起重排、重绘的操作
浏览器在处理重排时,会递归处理DOM节点,所以导致重排的成本高于重绘。
引起浏览器重排的操作:
- 调整窗口大小
- 字体大小
- 样式表变动
- 元素内容变化,尤其是输入控件
- css伪类激活,在用户交互过程中发生
- DOM操作,DOM元素增删、修改
- width、clientWidth、scrollTop等布局宽高的计算
在这些引起回流的操作中,6,7是JS相关的。因此开发人员需要注意:
- 避免大量的DOM操作
- 避免过多DOM布局属性的计算
注意:display:none会触发reflow,而visibility只会触发repaint,因为没有位置变化。
三、如何避免过多的Repaint和Reflow?
核心在于减少Repaint、Reflow的次数。
var s = document.body.style;
s.padding = "2px"; // 回流+重绘
s.border = "1px solid red"; // 再一次 回流+重绘
s.color = "blue"; // 再一次重绘
s.backgroundColor = "#ccc"; // 再一次 重绘
s.fontSize = "14px"; // 再一次 回流+重绘
// 添加node,再一次 回流+重绘
document.body.appendChild(document.createTextNode('abc!'));
Repaint和Reflow是不可避免的,只能说对性能的影响减到最小,给出下面几条建议:
- 避免逐条更改样式。建议集中修改样式,例如操作className。
- 避免频繁操作DOM。创建一个documentFragment或div,在它上面应用所有DOM操作,最后添加到文档里。设置display:none的元素上操作,最后显示出来。
- 避免频繁读取元素几何属性(例如scrollTop)。
- 绝对定位具有复杂动画的元素。绝对定位使它脱离文档流,避免引起父元素及后续元素大量的回流