重绘与重排及其性能优化

169 阅读2分钟

1. 概念

重绘(Repaint):当DOM节点的可见属性发生变化时,浏览器会重新描绘该元素。
重排(Reflow):当DOM节点的布局属性发生变化时,浏览器会重新计算属性,重新构建render树,之后再绘制每个节点,因此重排会引起重绘

2. 触发重排的操作

页面布局和元素的几何属性改变就会导致重排,如下列操作:

  • 调整窗口大小
  • 调整字体大小
  • 添加、删除可见的DOM元素
  • 改变元素的位置
  • CSS伪类的激活
  • width, clientWidth, scrollTop等布局宽高的计算

3. 渲染队列

一般来说,版本较新的浏览器都有渲染队列的机制。当一个操作引起重排或者重绘时,会新建一个渲染队列。如果接下来的操作同样是修改样式等引起重排重绘的操作,则会将操作入队。浏览器会批量渲染,因此只进行一次重排重绘。
示例代码:

//由于渲染队列机制,只进行一次重排重绘
div.style.left = '10px';
div.style.top = '10px';
div.style.width = '20px';
div.style.height = '20px';

倘若在修改操作中有读取或者其他操作,则就算后续仍有修改操作,也会另起新的渲染队列。如下面的代码:

//第一个渲染队列
div.style.left = '10px';
//读取操作,停止入队,开始重排重绘
console.log(div.offsetLeft);
//第二次重排重绘
div.style.top = '10px';
console.log(div.offsetTop);
//第三次重排重绘
div.style.width = '20px';
console.log(div.offsetWidth);
//第四次重排重绘
div.style.height = '20px';
console.log(div.offsetHeight);

因此优化的思路是分离读写操作:

//只有一次重排重绘
div.style.left = '10px';
div.style.top = '10px';
div.style.width = '20px';
div.style.height = '20px';

console.log(div.offsetLeft);
console.log(div.offsetTop);
console.log(div.offsetWidth);
console.log(div.offsetHeight);

实际上,以下属性或方法会刷新渲染队列,使浏览器立即开始渲染,因此在修改样式的过程中,要尽量避免使用这些属性和方法

  • offsetTop、offsetLeft、offsetWidth、offsetHeight
  • clientTop、clientLeft、clientWidth、clientHeight
  • scrollTop、scrollLeft、scrollWidth、scrollHeight
  • getComputedStyle()(IE中currentStyle)

    面试题目
//下面的操作会引起几次重排
div.style.left = div.offsetLeft + 1 + 'px';//1
div.style.top = div.offsetTop + 1 + 'px';//2

//读操作结束就执行写操作,造成两次重排
//优化:使用缓存,分类读写操作,仅进行1次重排
var curLeft = div.offsetLeft;
var curTop = div.offsetTop;

div.style.left = curLeft + 1 + 'px';
div.style.top = curTop + 1 + 'px';