全部学科
NodeJS全栈
nodejs
Python全栈
python
小程序首页
📅 2026-05-16 8 分钟 ✍️ juanwangdev

回流/重排与重绘底层机制

回流和重绘是浏览器渲染管线中的关键操作,频繁触发会严重影响页面性能。

核心概念

回流(Reflow)

元素几何属性变化时,浏览器重新计算元素位置和大小。

JavaScript
几何属性变化
    ↓
重新计算布局
    ↓
更新渲染树
    ↓
触发布局阶段

重绘(Repaint)

元素外观变化但不影响布局时,浏览器重新绘制元素。

JavaScript
外观属性变化
    ↓
跳过Layout
    ↓
直接Paint

性能开销对比

操作开销渲染阶段
回流Layout → Paint → Composite
重绘Paint → Composite
合成Composite

回流必然触发重绘,重绘不一定触发回流。

触发回流的操作

几何属性变化

JavaScript
// 尺寸变化
element.style.width = '200px';
element.style.height = '100px';
element.style.padding = '20px';
element.style.margin = '10px';
element.style.border = '1px solid #000';

// 位置变化
element.style.left = '100px';
element.style.top = '50px';

// 显示状态
element.style.display = 'block';
element.style.display = 'none';

DOM结构变化

JavaScript
// 添加/删除元素
parent.appendChild(child);
parent.removeChild(child);
parent.insertBefore(newChild, refChild);

// 内容变化
element.innerHTML = '<div>新内容</div>';
element.textContent = '新文本';

读取布局属性

JavaScript
// 强制同步布局(Force Synchronous Layout)
const width = element.offsetWidth;
const height = element.offsetHeight;
const left = element.offsetLeft;
const top = element.offsetTop;

// getComputedStyle
const style = getComputedStyle(element);
const fontSize = style.fontSize;

// getBoundingClientRect
const rect = element.getBoundingClientRect();

特定CSS属性

属性说明
width/height尺寸
padding/margin内/外边距
border边框
position定位
top/left/right/bottom偏移
display显示方式
font-size字体大小
text-align文本对齐
overflow溢出处理
float浮动

触发重绘的操作

外观属性变化

JavaScript
// 颜色变化
element.style.color = 'red';
element.style.backgroundColor = 'blue';
element.style.borderColor = 'green';

// 背景
element.style.backgroundImage = 'url(bg.png)';
element.style.backgroundSize = 'cover';

// 可见性
element.style.visibility = 'hidden';
element.style.visibility = 'visible';

// 轮廓
element.style.outline = '2px solid red';

// 阴影
element.style.boxShadow = '0 2px 4px rgba(0,0,0,0.2)';

只触发重绘的属性

属性说明
color文字颜色
background-color背景色
visibility可见性
outline轮廓
box-shadow阴影
opacity透明度
text-decoration文本装饰

布局抖动

什么是布局抖动

在循环中交替读写布局属性,导致反复强制同步布局。

JavaScript
// 错误:布局抖动
elements.forEach(el => {
  const height = el.offsetHeight;    // 读取 → 强制布局
  el.style.width = height + 'px';    // 写入 → 标记脏
});

// 浏览器行为:
// 1. 读取第一个元素的offsetHeight → 强制布局
// 2. 设置width → 标记脏
// 3. 读取第二个元素的offsetHeight → 强制重新布局
// 4. 设置width → 标记脏
// ... 循环N次

避免布局抖动

JavaScript
// 正确:批量读取,批量写入
const heights = elements.map(el => el.offsetHeight);  // 先批量读

elements.forEach((el, i) => {
  el.style.width = heights[i] + 'px';  // 再批量写
});
JavaScript
// 使用FastDOM模式
const reads = [];
const writes = [];

function measure(fn) {
  reads.push(fn);
}

function mutate(fn) {
  writes.push(fn);
}

function flush() {
  reads.forEach(fn => fn());
  reads.length = 0;

  writes.forEach(fn => fn());
  writes.length = 0;
}

// 使用
measure(() => {
  height = element.offsetHeight;
});

mutate(() => {
  element.style.width = height + 'px';
});

requestAnimationFrame(flush);

性能优化策略

1. 批量样式修改

CSS
// 错误:逐个修改
element.style.width = '100px';
element.style.height = '50px';
element.style.margin = '10px';

// 正确:使用class
element.classList.add('active');

// 或使用cssText
element.style.cssText = 'width:100px;height:50px;margin:10px;';

2. 脱离文档流操作

CSS
// 方法1:display:none
element.style.display = 'none';
// 执行多次DOM操作
element.style.display = 'block';

// 方法2:绝对定位脱离
element.style.position = 'absolute';
element.style.left = '-9999px';
// 执行操作
element.style.position = '';
element.style.left = '';

3. 使用transform/opacity

CSS
/* 错误:触发回流 */
.animated {
  position: absolute;
  left: 100px;
  top: 100px;
}

/* 正确:只触发合成 */
.animated {
  transform: translate(100px, 100px);
}
JavaScript
/* 错误:触发重绘 */
.fade {
  visibility: hidden;
  opacity: 0;
}

/* 正确:只触发合成 */
.fade {
  opacity: 0;
  will-change: opacity;
}

4. 使用will-change提示

text
.will-animate {
  will-change: transform, opacity;
}

过度使用will-change会占用过多GPU资源,只在需要时使用。

5. 使用ResizeObserver替代定时轮询

text
// 错误:定时轮询
setInterval(() => {
  if (element.offsetWidth !== lastWidth) {
    // 处理
  }
}, 100);

// 正确:ResizeObserver
const observer = new ResizeObserver(entries => {
  entries.forEach(entry => {
    console.log(entry.contentRect);
  });
});

observer.observe(element);

调试工具

Chrome DevTools

text
Performance面板 → 记录 → 查看Layout/Paint事件
  • 紫色:Layout事件
  • 绿色:Paint事件

强制布局调用栈

text
Elements → 选中元素 → 右键 → Force Layout

Performance Monitor

text
Ctrl+Shift+P → Show Performance Monitor

监控指标:

  • Layouts / sec
  • Style recalcs / sec
  • Paints / sec

要点总结

  1. 回流触发Layout,重绘只触发Paint,回流开销远大于重绘
  2. 读取布局属性会强制同步布局,避免在循环中交替读写
  3. 批量修改样式,使用class或cssText
  4. 使用transform/opacity替代几何属性变化
  5. 复杂动画元素脱离文档流或使用will-change

📝 发现内容有误?点击此处直接编辑

← 上一篇 合成层与渲染性能
下一篇 → 块级格式化上下文(BFC)与布局上下文原理
想查看更多题目和详细解析?
小程序提供完整的题库、模拟考试和详细解析
马上就来

长按或扫描二维码,立即体验

扫码体验小程序
马上就来
使用微信扫描二维码
立即体验完整题库