重排和重绘
在浏览器中,重排(Reflow)和重绘(Repaint)是渲染引擎的两个重要过程。它们主要用于响应 DOM 和 CSS 的更改,但触发条件和性能消耗不同:
一、重排(Reflow)
定义
重排指的是当页面的布局或几何属性发生变化时,浏览器需要重新计算元素的位置和尺寸,并重新构建渲染树。
触发重排的操作
重排通常由以下操作触发:
添加、删除或修改 DOM 元素
- 动态插入新节点或移除已有节点。
const div = document.createElement('div'); document.body.appendChild(div); // 触发重排
修改元素的几何属性
- 如
width
、height
、padding
、margin
、border
、top
、left
、font-size
等。 element.style.width = '200px'; // 触发重排
修改文档流
- 如更改
display
、position
、float
等。 element.style.display = 'block'; // 触发重排
读取影响布局的属性
- 如
offsetWidth
、offsetHeight
、clientWidth
、getBoundingClientRect()
等,这会强制触发重排以保证读取到的是最新的布局信息 console.log(element.offsetWidth); // 触发重排
浏览器窗口大小发生变化
- 浏览器窗口调整时会重新计算布局。
性能消耗
重排是一个性能开销较大的操作,因为它会导致整个页面或部分页面的重新计算,尤其是在涉及复杂布局或大量 DOM 节点时。
二、重绘(Repaint)
定义
重绘指的是当页面的视觉样式发生变化但布局没有变化时,浏览器会重新绘制元素的外观,而无需重新计算位置或尺寸。
触发重绘的操作
重绘通常由以下操作触发:
修改元素的外观属性
- 如
color
、background-color
、visibility
、border-color
、box-shadow
等。 element.style.backgroundColor = 'red'; // 触发重绘
修改透明度或显示状态
- 如
opacity
、visibility
(非display: none
)。 element.style.opacity = '0.5'; // 触发重绘
性能消耗
重绘只涉及图层的重新绘制,通常比重排开销小,因为它不涉及布局计算和文档流的重新生成。
三、重排和重绘的区别
类型 | 触发条件 | 性能开销 |
---|---|---|
重排 | 几何属性和布局变化(宽高、位置、文档流) | 高 |
重绘 | 视觉样式变化(颜色、背景、透明度) | 较低 |
四、如何减少重排和重绘
减少重排
批量修改样式
- 避免多次修改样式属性,可以通过
className
或cssText
一次性修改。element.style.cssText = "width: 100px; height: 100px;";
离线操作 DOM
- 使用
DocumentFragment
或将元素设置为display: none
后再操作,最后统一插入或显示。 const fragment = document.createDocumentFragment(); for (let i = 0; i < 100; i++) { const div = document.createElement('div'); fragment.appendChild(div); } document.body.appendChild(fragment); // 只重排一次
避免触发强制同步布局
- 避免频繁读取和写入布局信息,尽量将读取和写入操作分开。
const width = element.offsetWidth; // 读取布局信息element.style.width = width + '10px'; // 写入新样式
使用 transform
和 opacity
- 对需要移动或动画的元素,尽量使用
transform
或opacity
,避免直接操作top
、left
等布局属性。
减少重绘
减少复杂的视觉效果
- 避免使用过多的阴影、渐变等效果。
避免过度的样式修改
- 避免频繁触发非必要的样式变化。
合并绘制区域
- 尽量将需要改变样式的多个元素合并到一个容器中,减少重绘的次数。
五、重排与重绘的常见优化方式:分层
通过GPU 加速(CSS will-change
或 transform
)将频繁操作的部分单独分层,可以减少重排和重绘对主渲染线程的影响。
示例:通过 GPU 加速优化
.element { will-change: transform, opacity; /* 告诉浏览器提前优化 */}
// 动画操作只影响 transform 和 opacity,不会触发重排element.style.transform = 'translateX(100px)'; element.style.opacity = '0.5';