← 返回题目列表

重排、重绘和合成有什么区别?

高频 中等 第 8 / 30 题 更新于 2026/07/28
浏览器重排重绘合成

简化版

重排是重新计算元素几何位置和尺寸,成本最高;重绘是重新绘制颜色、背景、阴影等视觉效果,不一定改变布局;合成是把已有图层组合到屏幕,通常成本较低。优化时应减少布局属性变化,优先使用 transformopacity 做动画。

详细版

重排会发生在布局变化时,比如修改宽高、字体大小、边距、定位、插入删除 DOM、读取某些布局属性后又写样式。

重绘发生在视觉变化但布局不变时,比如修改颜色、背景色、阴影。

合成通常发生在图层级别,比如使用 transformopacity,浏览器可以不重新布局和绘制,只由合成线程处理。

性能上一般是:重排成本最高,重绘次之,合成较低。但创建太多图层也会占用内存,不是越多越好。

完整版教学

一、重排是什么

重排也叫回流,指浏览器重新计算元素在页面中的位置和尺寸。它会影响布局树,严重时可能牵连大量节点。

常见触发条件:

  • 修改 widthheightmarginpadding
  • 改变字体大小。
  • 插入或删除 DOM。
  • 修改定位属性。
  • 改变窗口大小。
  • 在布局已失效后读取 offsetWidthclientHeight 等几何属性,可能强制刷新布局。

重排之后通常还会伴随重绘。

二、重绘是什么

重绘指元素几何信息不变,但外观发生变化,需要重新绘制像素。

比如:

  • 修改 color
  • 修改 background-color
  • 修改 box-shadow
  • 修改 visibility

重绘不一定引发布局重新计算,但仍然要消耗绘制成本。

三、合成是什么

浏览器可能把页面拆成多个图层。某些属性变化可以只在合成阶段完成,例如 transformopacity

这类动画通常更流畅,因为它们可以避开主线程上的布局和绘制压力。但如果滥用 will-change 或制造过多图层,会增加显存占用,反而拖慢页面。

四、面试追问与工程落地

面试官常问:“如何避免强制同步布局?”

强制同步布局常发生在先写样式、马上读布局:

el.style.width = '200px'
const width = el.offsetWidth

浏览器为了给出准确结果,必须立刻完成布局计算。优化方式是批量读、批量写,避免读写交错,也可以使用 requestAnimationFrame 安排视觉更新。

工程中动画优先用 transform,列表更新批量操作 DOM,复杂页面避免频繁读取布局属性。

五、失效、强制布局与读写批处理

修改几何样式会把相关布局标记为失效,但浏览器可能延迟到渲染阶段统一计算。只有在布局已失效后,又读取必须返回最新几何值的 API,才会迫使浏览器提前同步布局;单纯读取 offsetWidth 并不意味着每次都强制重排。

// 容易抖动:每轮写后立即读
for (const el of items) {
  el.style.width = '200px'
  console.log(el.offsetWidth)
}

// 更好:先批量读,再批量写
const widths = items.map(el => el.offsetWidth)
requestAnimationFrame(() => items.forEach(el => { el.style.width = '200px' }))
变化通常涉及阶段例子
几何变化style → layout → paint → compositewidth、字体、插入节点
像素外观变化style → paint → composite背景、阴影
合成属性变化style/commit → composite,视情况transform、opacity
几何读取若有未决失效则同步 layoutgetBoundingClientRect

六、帧预算、图层与合成的代价

60Hz 一帧约 16.7ms。若一次布局 8ms、绘制 7ms、JS 6ms,总计 21ms,就至少错过一帧;布局范围越大、绘制区域越大,成本越高,所以要用 DevTools 的 Layout、Paint 和 Layers 证据定位。

transformopacity 动画常可在已有合成层上避免重复布局/绘制,但不是语言层保证。首次提升图层可能需要光栅化,复杂滤镜、遮罩、巨大图层或内存压力也会增加成本;will-change 应短期、按需使用,而不是给所有节点常驻。

假设 100 个 1000×1000 像素的 RGBA 图层,未计压缩与额外缓冲,理论像素内存约 100 × 1000 × 1000 × 4 ≈ 400MB。这说明“多建层让动画更快”可能把问题变成显存占用、上传和合成开销。

内容隔离、固定尺寸容器、批量 DOM 更新和虚拟列表都可缩小失效范围。优化目标不是消灭所有 layout/paint——页面必须布局和绘制——而是避免重复、同步和大范围工作。

记忆钩子:写操作制造“布局欠账”,紧接着的几何读取要求立刻还账;先读后写才能让浏览器批量结算。

七、常见误区与追问

  • 误区:读取 offsetWidth 每次都会触发强制重排。 只有存在待处理的样式/布局失效且读取需要最新值时才会同步计算。
  • 误区:transformopacity 在任何元素上都只走合成。 是否提升图层和是否需要绘制由浏览器、属性与页面上下文决定,应实测。
  • 误区:图层越多性能越好。 图层占用内存并增加光栅、上传和合成管理成本,滥用会适得其反。
  • 追问:重排为什么可能影响兄弟或后代? 布局约束相互依赖,父级尺寸与流式排版变化会传播到相关盒子。
  • 追问:如何发现 layout thrashing? Performance 面板会显示脚本触发的重复 Layout,可回溯到交错读写的调用栈。
  • 追问:rAF 能自动消除强制布局吗? 不能;若 rAF 内仍写后读,依然可能同步布局,它只是对齐帧时机。
  • 追问:什么时候使用 will-change 已测得即将发生的合成动画可在开始前短时提示,结束后移除,避免常驻大量层。

八、加强记忆

重排、重绘、合成按“几何、像素、图层”区分,但性能取决于影响范围和时序。布局失效可以延迟批处理,写后立刻读几何才容易强制同步;动画优先评估 transform/opacity,却要验证图层与显存成本。用 Performance、Paint 和 Layers 面板测量,再通过先读后写、缩小范围和控制图层优化。