Lovi.space

前端工程

浏览器渲染管线:一次样式更新为何会造成卡顿

从告警看板的实时刷新出发,理解 DOM、布局、绘制与合成之间真正昂贵的步骤。

·1 min read
#前端#CSS#性能优化#浏览器

核心洞察:动画是否流畅,不取决于“用了动画库”,而取决于每一帧是否避开了昂贵的布局与绘制工作。 霓虹港的实时告警看板每秒更新一次。初版在卡片上直接修改 topleft、阴影和尺寸,桌面端尚可,低性能设备却开始掉帧。理解浏览器如何把代码变成像素,是定位问题的最短路径。

从 DOM 到屏幕的五步

浏览器先解析 HTML 得到 DOM,解析 CSS 得到 CSSOM,再组合出 Render Tree。随后计算几何位置的 Layout、把像素画到图层的 Paint,最后由 Composite 把图层提交到屏幕。 并非所有变化成本相同:改变宽高、边距、字体通常会触发布局;改变背景、阴影可能触发绘制;transformopacity 在条件满足时通常只需要合成。这就是位移动画更推荐写成 transform: translateY() 的原因。

强制同步布局最隐蔽

当脚本先写样式、立刻读取 offsetHeightgetBoundingClientRect(),浏览器为了给出正确数值,不得不提前完成布局。这种读写交错在循环中出现时,就会造成布局抖动。

const heights = cards.map((card) => card.offsetHeight);
cards.forEach((card, index) => {
card.style.transform = `translateY(${heights[index]}px)`;
});

先集中读取、再集中写入,让浏览器拥有合并工作的空间。视觉更新可放进 requestAnimationFrame,但它不是性能魔法:里面仍然不该做重计算或大量 DOM 操作。

DevTools 要看什么

Performance 面板中,长时间的紫色 Layout、绿色 Paint 或黄色 JavaScript 都是线索。Rendering 面板的 Paint Flashing 能暴露“本以为只移动了一个角标,却重绘整张卡片”的问题。

症状 首要检查
拖动卡顿 Layout 与强制重排
滚动掉帧 主线程长任务、监听器
动画模糊或闪烁 图层创建与绘制区域
首屏慢 阻塞资源与关键渲染路径
## 结语 性能优化不是把所有属性都换成 `transform`,而是理解每一帧工作的代价。先在性能时间线上找到瓶颈,再缩小影响范围;视觉效果才不必以流畅度为代价。

读到这里,说明你也在认真对待这个问题。

讨论与反应

⌘ K