理解重绘和回流
大约 4 分钟
重绘(Repaint)和回流(Reflow)详解
重绘和回流是浏览器渲染网页时的两个重要概念,理解它们对于优化网页性能至关重要。
1. 浏览器渲染流程
在深入理解重绘和回流之前,先了解浏览器的渲染流程:
- 解析 HTML → 构建 DOM 树
- 解析 CSS → 构建 CSSOM 树
- 合并 DOM 和 CSSOM → 构建渲染树(Render Tree)
- 布局(Layout) → 计算元素的几何信息(位置、大小)
- 绘制(Paint) → 将元素绘制到屏幕上
- 合成(Composite) → 将各层合并成最终图像
2. 回流(Reflow)
定义
回流也叫重排,是浏览器重新计算页面元素的几何属性(位置、大小)并重新构建渲染树的过程。
触发回流的情况:
页面首次渲染
窗口尺寸改变
window.addEventListener('resize', function() { // 会触发回流 });元素几何属性改变
.element { width: 100px; /* 改变这些属性会触发回流 */ height: 100px; margin: 10px; padding: 20px; border: 1px solid #000; position: absolute; left: 50px; top: 50px; }DOM 操作
// 添加、删除、更新 DOM 节点 const newElement = document.createElement('div'); document.body.appendChild(newElement); // 触发回流 // 获取某些属性时也会触发回流 const width = element.offsetWidth; // 强制回流以获取准确值 const height = element.offsetHeight;字体大小改变
内容改变(文本或图片等)
激活 CSS 伪类(如 :hover)
操作 class 属性
脚本操作 DOM
计算 offsetWidth 和 offsetHeight 属性
设置 style 属性的值
3. 重绘(Repaint)
定义
重绘是当元素的外观发生变化但不影响布局时,浏览器重新绘制元素的过程。
触发重绘的情况:
颜色改变
.element { color: red; /* 触发重绘 */ background-color: blue; /* 触发重绘 */ border-color: green; /* 触发重绘 */ box-shadow: 0 0 5px rgba(0,0,0,0.5); /* 触发重绘 */ }可见性改变
.element { visibility: hidden; /* 触发重绘 */ opacity: 0.5; /* 触发重绘 */ }背景图片改变
文本内容改变(不影响布局时)
⚠️ 注意:回流一定会触发重绘,但重绘不一定会触发回流。
4. 性能影响
回流的代价更高:
// 高代价操作示例
const element = document.getElementById('myElement');
// 每次访问 offset 属性都会触发回流
console.log(element.offsetWidth); // 回流
element.style.width = '200px'; // 回流
console.log(element.offsetHeight); // 回流
element.style.height = '200px'; // 回流
// 更好的方式:批量操作
element.style.cssText = 'width: 200px; height: 200px;'; // 一次回流5. 优化策略
1. 批量修改样式
// 不好的做法
element.style.left = '10px';
element.style.top = '10px';
element.style.width = '100px';
element.style.height = '100px';
// 好的做法1:使用 cssText
element.style.cssText = 'left: 10px; top: 10px; width: 100px; height: 100px;';
// 好的做法2:使用 class
.element.classList.add('new-style');
// CSS 中定义
.new-style {
left: 10px;
top: 10px;
width: 100px;
height: 100px;
}2. 离线操作 DOM
// 不好的做法
const container = document.getElementById('container');
for (let i = 0; i < 1000; i++) {
const div = document.createElement('div');
container.appendChild(div); // 每次都触发回流
}
// 好的做法:使用文档片段
const fragment = document.createDocumentFragment();
for (let i = 0; i < 1000; i++) {
const div = document.createElement('div');
fragment.appendChild(div);
}
container.appendChild(fragment); // 只触发一次回流3. 缓存布局属性
// 不好的做法
for (let i = 0; i < 100; i++) {
// 每次循环都触发回流
element.style.left = (element.offsetLeft + 10) + 'px';
}
// 好的做法
const left = element.offsetLeft; // 只触发一次回流
for (let i = 0; i < 100; i++) {
element.style.left = (left + 10 * i) + 'px';
}4. 使用 transform 和 opacity
/* 不好的做法:触发回流 */
.bad-animation {
transition: left 0.3s, top 0.3s;
}
/* 好的做法:只触发合成 */
.good-animation {
transform: translate(100px, 100px);
transition: transform 0.3s;
}5. 合理使用 position: absolute/fixed
/* 使用绝对定位避免影响其他元素 */
.floating-element {
position: absolute;
/* 改变这个元素不会影响其他元素的布局 */
}6. 使用 requestAnimationFrame
// 不好的做法
function animate() {
element.style.left = (parseInt(element.style.left) + 1) + 'px';
if (parseInt(element.style.left) < 500) {
setTimeout(animate, 16); // 可能导致回流堆积
}
}
// 好的做法
function animate() {
element.style.left = (parseInt(element.style.left) + 1) + 'px';
if (parseInt(element.style.left) < 500) {
requestAnimationFrame(animate); // 浏览器优化的动画帧
}
}6. 检测工具
使用浏览器开发者工具:
Chrome DevTools Performance 面板
- 记录页面操作
- 查看回流和重绘事件
- 分析性能瓶颈
Firefox 开发者工具
- 性能面板可以显示重排和重绘
JavaScript 检测:
// 监听回流事件(部分浏览器支持)
const observer = new MutationObserver(function(mutations) {
console.log('DOM 变化,可能触发回流');
});
observer.observe(document.body, {
attributes: true,
childList: true,
subtree: true
});7. 常见优化场景
动画优化:
/* 使用 will-change 提示浏览器 */
.optimized-animation {
will-change: transform;
transform: translateZ(0); /* 创建新的合成层 */
}
/* 使用 transform3d 强制硬件加速 */
.hardware-accelerated {
transform: translate3d(0, 0, 0);
}列表操作优化:
// 虚拟滚动实现
class VirtualList {
constructor(container, items) {
this.container = container;
this.items = items;
this.visibleItems = [];
this.render();
}
render() {
// 只渲染可见区域的元素,减少 DOM 节点
// 降低回流和重绘的频率
}
}理解并优化重绘和回流是前端性能优化的重要环节。通过合理的代码编写和优化策略,可以显著提升页面渲染性能,提供更好的用户体验。