← 返回题目列表

什么是重排和重绘?如何减少它们?

高频 中等 第 8 / 28 题 更新于 2026/07/27
浏览器渲染CSS重排重绘性能优化

简化版

重排是布局尺寸或位置变化导致浏览器重新计算布局,重绘是视觉样式变化导致重新绘制。重排通常比重绘更重。减少方式包括批量修改 DOM、避免频繁读写布局、使用 transform/opacity 做动画、减少复杂选择器和大范围布局影响。

详细版

会触发重排的操作:

  • 添加、删除 DOM。
  • 修改元素尺寸、位置、字体、边距。
  • 改变窗口大小。
  • 读取某些布局属性后又写样式,比如 offsetWidthgetBoundingClientRect()

只触发重绘的操作:

  • 修改颜色、背景色、阴影等不影响布局的属性。

优化思路:

  1. DOM 修改合并处理,避免循环里反复插入。
  2. 读布局和写样式分离,避免强制同步布局。
  3. 动画优先用 transformopacity
  4. 对复杂区域使用合理隔离,比如 contain

完整版教学

一、浏览器渲染的大致流程

浏览器从 HTML/CSS 生成 DOM 和 CSSOM,然后合成渲染树,计算布局,绘制图层,最后合成显示。重排发生在布局阶段,重绘发生在绘制阶段。

如果元素尺寸或位置变了,浏览器必须重新算它和相关元素的位置,这就是重排。如果只是颜色变了,布局没变,只需要重新画出来,这就是重绘。

二、为什么重排更贵

重排可能影响一串元素。比如你修改一个列表第一项的高度,后面很多项的位置都要重新计算。页面越复杂,影响范围越大,成本越高。

重绘一般不需要重新计算几何位置,但仍然可能消耗绘制成本,尤其是大面积阴影、滤镜、渐变等。

三、强制同步布局是性能坑

下面这种代码容易导致浏览器一边写样式一边被迫读最新布局:

for (const el of list) {
  el.style.width = '200px';
  console.log(el.offsetWidth);
}

浏览器为了返回准确的 offsetWidth,可能不得不立刻完成前面的样式计算和布局。更好的做法是先读后写,或者批量写入。

四、动画为什么推荐 transform 和 opacity

left/top/width/height 通常会影响布局,而 transformopacity 很多时候可以在合成层处理,不必频繁触发布局计算。

.panel {
  transform: translateX(0);
  transition: transform .2s ease;
}
.panel.open {
  transform: translateX(100px);
}

这比不断改 left 更适合高频动画。

五、面试追问与工程落地

面试官常会给代码让你判断是否触发强制同步布局。比如先改样式再读 offsetHeight,浏览器为了返回准确值,可能立即刷新样式和布局。优化思路是把读操作集中在前面,把写操作集中在后面,或者放进 requestAnimationFrame 分帧处理。

还会追问 will-change 能不能随便加。答案是不能。will-change: transform 可以提前提示浏览器做优化,但会增加内存和图层管理成本。只应该给即将发生动画的少量元素使用,并在动画结束后移除或避免长期滥用。

工程定位性能问题时,不要猜。应该用 Chrome DevTools Performance 看 Layout、Paint、Composite 的耗时,用 Rendering 面板观察 paint flashing。只有确认瓶颈在布局或绘制,优化才有方向。

六、用一帧预算理解优化优先级

60Hz 屏幕为例,每帧总预算约为 1000 / 60 ≈ 16.7ms,这段时间还要容纳输入处理、JavaScript、样式、布局、绘制和合成。若脚本已占 9ms,布局与绘制又占 10ms,本帧总耗时至少 19ms,就可能错过刷新节点;因此“重排比重绘贵”只能作为经验,真正瓶颈要用性能记录测量。

JavaScript → Style → Layout → Paint → Composite
                         ↑         ↑          ↑
改宽高常走完整链路      改颜色常从 Paint 继续   transform/opacity 可能仅合成
操作可能经过的主要阶段注意点
修改 width、DOM 结构Layout → Paint → Composite影响范围取决于布局关系
修改 color、阴影Paint → Composite大面积复杂效果仍可能昂贵
修改 transformopacity常可只 Composite是否提升图层由浏览器决定
写样式后立即读几何量可能强制同步 Style/Layout在同一任务中批量读、批量写

CSS 属性与渲染阶段不是跨浏览器、跨场景的绝对对照表;应以目标浏览器的 DevTools 记录验证,而不是把“transform 一定走 GPU”当成保证。

七、常见误区与追问

  • 误区:重绘一定比重排便宜。 大面积滤镜、阴影或高分辨率区域的绘制可能非常昂贵,成本必须结合影响范围和设备测量。
  • 误区:使用 transform 就一定创建 GPU 图层且永不卡顿。 图层提升是浏览器实现策略,合成、纹理上传、内存和主线程脚本仍可能造成卡顿。
  • 误区:给所有动画元素长期加 will-change 能提升性能。 过度提示会增加图层与内存管理成本,应限于即将变化的少量元素。
  • 追问:什么是 layout thrashing? 代码交替写样式与读取 offsetWidth 等几何信息,迫使浏览器反复同步刷新样式和布局。
  • 追问:requestAnimationFrame 会自动消除重排吗? 不会,它只把回调安排在渲染前;回调内部若读写交错,仍可能触发强制同步布局。
  • 追问:如何判断问题在 Layout、Paint 还是 Composite? 使用 Performance 录制实际交互,查看各阶段耗时、调用栈、影响区域和帧时间。
  • 追问:contain 为什么可能改善性能? 合理的 layout、paint 等 containment 能缩小变更传播范围,但也会改变尺寸、定位或裁剪语义,不能盲加。

八、加强记忆

重排是“重新算位置大小”,重绘是“重新上色画图”。性能优化的关键是少动布局、多做批量、动画用 transform/opacity,尤其避免在循环里交替读布局和写样式。