重绘与重排(Reflow and Repaint)
一篇写的很好的对于了解重绘与重排的文章:
https://dev.to/gopal1996/understanding-reflow-and-repaint-in-the-browser-1jbg
基本概念
重排
发生在浏览器重新计算网页的某些部分的位置和几何形状时(更改会影响部分或整个页面的布局),重排会导致DOM中所有子元素和祖先元素重排。这通常会紧接着重绘(repaint),即浏览器重新绘制网页以显示更新后的视觉效果。
重绘
浏览器重新绘制网页以显示由 UI 更改引起的视觉更新且不会影响元素布局时发生(例如:可见性、背景颜色、轮廓等),这通常是在重排之后发生的,重排是浏览器重新计算网页的某些部分的位置和几何形状。
总结
重排与重绘都是非常消耗性能的操作,大多数重排都会导致页面的重新渲染,是导致DOM脚本(Script)加载速度慢的主要原因之一,尤其是在低性能设备上。
什么会导致重绘与重排
- 添加、删除、更新DOM节点发生重排
- 用
display:none
隐藏DOM元素:会导致重排和重绘 - 用
visibility:hidden
隐藏DOM元素:只会导致重绘,因为没有布局或位置变化 - 移动DOM节点, 为DOM节点设置动画:触发重排和重绘
- 调整窗口大小:触发重排
- 更改字体样式会改变元素的几何形状。这意味着它可能会影响页面上其他元素的位置或大小,这两者都需要浏览器执行重排。一旦这些布局操作完成,任何已经修改的像素都需要重绘,即会触发重排与重绘
- 添加或删除样式表将导致重排/重绘
- …
var bodyStyle = document.body.style; // cache
bodyStyle.padding = "20px"; // reflow, repaint
bodyStyle.border = "10px solid red"; // reflow, repaint
bodyStyle.color = "blue"; // repaint only, no dimensions changed
bstyle.backgroundColor = "#cc0000"; // repaint
bodyStyle.fontSize = "2em"; // reflow, repaint
// new DOM element - reflow, repaint
document.body.appendChild(document.createTextNode('Hello!'));
操作DOM的脚本(Script)是昂贵的操作,因为每次修改文档或文档的一部分时,它们都会重新计算。正如我们从引发重排的许多事情中看到的那样,它每秒可以发生成千上万次
减少重排/重绘
减少重排/重绘对用户体验的负面影响的策略是简单地减少重排和重绘,减少对样式信息的请求,这样浏览器就可以优化重排。那么该如何实现?
- 不要一个接一个地改变CSS样式。为了保持可维护性,最好更改类名,而不是样式。如果样式是动态的,可以编辑CSSText属性
// bad
var left = 10,
top = 10;
el.style.left = left + "px";
el.style.top = top + "px";
// better
el.className += " theclassname";
// or when top and left are calculated dynamically...
// better
el.style.cssText += "; left: " + left + "px; top: " + top + "px;";
- 批量DOM更改
- 使用
documentFragment
保存临时更改 - 克隆、更新、替换节点
- 使用
display:none
隐藏元素(1次重排,1次重绘),然后再添加100个更改,然后再恢复显示(共2次重排,2次重绘)
- 不要反复请求计算样式,将它们缓存到变量中
- 多次读/写(如元素的height属性)
- 多次从DOM写入,然后读取,导致文档重排
- 读取(缓存)、写入(使布局无效)、读取(触发布局)。
- 解决:先读所有内容,然后再写所有内容
性能差的代码:
var box1Height = document.getElementById('box1').clientHeight;
document.getElementById('box1').style.height = box1Height + 10 + 'px';
var box2Height = document.getElementById('box2').clientHeight;
document.getElementById('box2').style.height = box2Height + 10 + 'px';
var box3Height = document.getElementById('box3').clientHeight;
document.getElementById('box3').style.height = box3Height + 10 + 'px';
var box4Height = document.getElementById('box4').clientHeight;
document.getElementById('box4').style.height = box4Height + 10 + 'px';
var box5Height = document.getElementById('box5').clientHeight;
document.getElementById('box5').style.height = box5Height + 10 + 'px';
var box6Height = document.getElementById('box6').clientHeight;
document.getElementById('box6').style.height = box6Height + 10 + 'px';
优化的代码:
var box1Height = document.getElementById('box1').clientHeight;
var box2Height = document.getElementById('box2').clientHeight;
var box3Height = document.getElementById('box3').clientHeight;
var box4Height = document.getElementById('box4').clientHeight;
var box5Height = document.getElementById('box5').clientHeight;
var box6Height = document.getElementById('box6').clientHeight;
document.getElementById('box1').style.height = box1Height + 10 + 'px';
document.getElementById('box2').style.height = box2Height + 10 + 'px';
document.getElementById('box3').style.height = box3Height + 10 + 'px';
document.getElementById('box4').style.height = box4Height + 10 + 'px';
document.getElementById('box5').style.height = box5Height + 10 + 'px';
document.getElementById('box6').style.height = box6Height + 10 + 'px';
原因:通过批量更改DOM,减少重排操作