浏览器通过一个管道将 CSS 更改转换为像素,不同的更改会触发不同的(或多或少昂贵的)阶段:
text
Style → Layout (reflow) → Paint → Composite
- Layout / reflow — 重新计算几何(位置/大小)。最昂贵 — 更改一个元素可能会强制重新计算许多其他元素。
- Paint / repaint — 填充像素(颜色、文本、阴影)。比 layout 更便宜。
浏览器通过一个管道将 CSS 更改转换为像素,不同的更改会触发不同的(或多或少昂贵的)阶段:
Style → Layout (reflow) → Paint → Composite
/* ❌ trigger LAYOUT (reflow) — costly, recalculates geometry every frame */
width, height, top, left, margin, padding, font-size
/* ⚠️ trigger PAINT — repaint pixels */
color, background, box-shadow, border-radius
/* ✅ trigger only COMPOSITE — GPU, no layout/paint */
transform, opacity
这是唯一最重要的性能规则:对 transform 和 opacity 进行动画,而不是 width/top/margin。 对 layout 属性进行动画会在每一帧上重新运行 layout(卡顿);transforms 由 GPU 合成(平滑 60fps)。
/* ❌ janky */ @keyframes a { to { left: 300px; width: 200px; } }
/* ✅ smooth */ @keyframes b { to { transform: translateX(300px) scaleX(2); } }
.animated { will-change: transform; } /* promotes to its own GPU layer ahead of time */
仅在即将进行动画的元素上使用 will-change — 过度使用会通过创建过多图层来浪费内存。
// ❌ read-write-read-write forces multiple synchronous reflows
for (const el of els) { el.style.width = el.offsetWidth + 10 + "px"; }
// ✅ batch reads, then writes
const widths = els.map(el => el.offsetWidth);
els.forEach((el, i) => el.style.width = widths[i] + 10 + "px");
- contain: layout/paint — isolate a subtree so changes don't reflow the whole page
- content-visibility: auto — skip rendering offscreen content
- minimize deep selectors and huge unused stylesheets
渲染性能主要是关于不重复触发 layout。
了解 transform/opacity 是仅用于合成的(便宜),而 width/top/margin 强制 reflow(昂贵) — 加上避免 layout thrashing 和使用 contain/content-visibility — 这就是使动画保持在 60fps 并让页面感觉快速的原因。