概念解析
页面渲染的流程如上,更多内容可了解chrome渲染流程。
DOM结构或者DOM的样式修改可能会引起页面再次执行上述部分流程。
重排
DOM的几何信息(位置和尺寸大小)发生改变,需要重新计算DOM的几何信息,再将其绘制出来的过程叫重排(Relayout),也叫回流(Reflow)。
比如width、position、float。
重绘
DOM的外观发生变化但没有改变布局,重新把外观绘制出来的过程叫重绘(Repaint)。
比如background-color、color、opacity。
可在工具中查看到重排、重绘的时间
重排、重绘对页面性能的影响
流程
从概念中我们知道:
DOM重排,则会触发重新生成布局树(Layout)及其之后的操作。
DOM重绘,则会触发重新绘制(Paint)及其之后的操作。和重排相比,少了布局和分层两个阶段。
以上可知,重排和重绘会让渲染进程、GPU进程、浏览器主进程再次工作,占用资源(主要是渲染进程的CPU计算)。其中,重排的的开销更大。
Demo
以重排为例,来看一下DOM更新的开销
Demo1: 多次访问 + 多次修改
<style>
#container{
word-break: break-word;
}
</style>
<div id="container"></div>
<script>
let times = 20000;
console.time(1);
for(let i = 0; i < times; i++){
const node = document.getElementById('container');
node.innerHTML += i;
}
console.timeEnd(1);
</script>
结果
1: 5904.803955078125 ms。
多次修改DOM的innerHTML,内容增多,短时间多次重排,需要(相对)长时间使用渲染进程(CPU计算完成后再生成图片)。在图片合成前,页面显示空白。
2.4 对应的是CPU列,页面渲染完成后,又会变成0。
GCP进程的CPU在这过程中也会增大然后又减小。
Demo2: 多次访问 + 一次修改
<script>
let times = 20000;
console.time(2);
let content = '';
let node = null;
for(let i = 0; i < times; i++){
node = document.getElementById('container');
content += i;
}
node.innerHTML = content;
console.timeEnd(2);
</script>
结果 2: 8.8330078125 ms (每次都不一样,最大没超过17ms)。
从以上两个demo可知,减少重排的次数能明显减少渲染进程执行时间(script在渲染进程中执行)。
Demo3: 一次访问 + 一次修改
<script>
let times = 20000;
console.time(3);
const node = document.getElementById('container');
let content = '';
for(let i = 0; i < times; i++){
content += i;
}
console.timeEnd(3);
</script>
结果 3: 1.93896484375 ms (最大没超过9,多数是1、2左右)。
获取DOM也是有消耗的,如果后续还要用到,可以获取一次,保存在内存中。
减少重排、重绘的方法
-
如果要改多个属性,一起设置而不是单个多次修改
比如可以用class或者dom.style.cssText=xx,而不是多个dom.style.xx=xx -
合理利用特殊样式属性,将重排改成合成(见后文)或减少对其他元素的布局影响
如果节点有动画,可以考虑用用translateY代替height,或者给元素加上position: absloute|fixed -
使用框架
框架用的虚拟DOM,会先计算出所有变化,然后再进行渲染。
其他
有什么属性修改后既不会改变几何信息也不会改变外观信息的吗?
有,CSS的tranform属性,这类属性变更后重新绘制的过程叫合成。
DOM合成,则会触发栅格化及其之后的操作。和重排和重绘相比,此过程绘制步骤更少,且不使用渲染进程的主线程,效率更高。