Appearance
浏览器从收到资源到渲染出页面的完整流程
更新: 6/28/2026 字数: 0 字 时长: 0 分钟

浏览器拿到服务器返回的 HTML 字节流后,到屏幕上画出页面,中间是一条固定的流水线:解析 HTML 建 DOM → 解析 CSS 建 CSSOM → 合并成渲染树 → 布局算位置 → 绘制成像素 → 合成上屏。 这条链路在 Chrome(Blink)、Safari(WebKit)、Firefox(Gecko)里大同小异,业界统称"关键渲染路径(Critical Rendering Path)"。下面按真实执行顺序逐步拆解。
第一步:解析 HTML,构建 DOM 树
浏览器拿到的是一串字节,先经过"字节 → 字符 → 词法标记(Token)→ 节点对象"的解析过程,最终把 HTML 组织成一棵 DOM 树(文档对象模型)。
- 做什么:从上到下读取 HTML,每遇到一个标签就生成一个节点,按照标签的嵌套关系组织成树形结构。
<html>是根,往下挂<head>、<body>,再往下是各种元素节点和文本节点。 - 作用:DOM 树是页面内容的结构化表示,是后续一切渲染和 JS 操作的基础。
document.getElementById操作的就是这棵树。 - 关键点:HTML 解析器有很强的容错能力,标签没闭合、写错了也会尽量修复。解析是边下载边解析的,不必等整个 HTML 下载完。
第二步:解析 CSS,构建 CSSOM 树

解析 HTML 时遇到 <link> 外链样式或 <style> 内联样式,浏览器会并行下载并解析 CSS,构建出 CSSOM 树(CSS 对象模型)。
- 做什么:把所有 CSS 规则解析成树形结构,并完成样式的层叠计算——每个节点最终生效的样式,是由继承、特异性、声明顺序综合算出来的。
- 作用:CSSOM 记录了每个元素该长什么样(颜色、字号、盒模型尺寸等),与 DOM 描述的"有什么内容"互补。
- 关键阻塞点:CSS 是渲染阻塞资源。浏览器必须等 CSSOM 完整构建好才能进入渲染树阶段——因为不知道完整样式就没法正确绘制。所以 CSS 应尽早、尽快加载,避免大体积 CSS 拖慢首屏。
第三步:JS 的介入与解析阻塞

这一步穿插在解析过程中,是性能问题高发区。HTML 解析时遇到 <script> 标签,默认行为是:
- 做什么:暂停 HTML 解析,先下载并执行脚本,执行完才继续解析后面的 HTML。
- 为什么阻塞:JS 可以通过
document.write或 DOM API 修改文档结构,浏览器不知道脚本会不会改 DOM,只能停下来等它跑完,否则解析结果可能作废。这就是 JS 阻塞解析。 - 额外依赖:如果脚本前面有还没加载完的 CSS,脚本会等 CSSOM 构建完才执行(因为 JS 可能读取样式信息)。所以 CSS 也会间接阻塞 JS。
- 优化手段:
defer:脚本异步下载,等 HTML 解析完、DOMContentLoaded 前按顺序执行,不阻塞解析。async:脚本异步下载,下载完立即执行(可能打断解析),执行顺序不保证,适合无依赖的独立脚本。- 把
<script>放在<body>末尾,也能避免阻塞主体内容解析。
第四步:合并生成渲染树(Render Tree)
DOM 树和 CSSOM 树都准备好后,浏览器把两者合并成 渲染树。
- 做什么:遍历 DOM 树,给每个可见节点匹配上 CSSOM 里对应的样式,生成渲染树的节点。
- 作用:渲染树只包含真正要显示的内容,是布局和绘制的直接依据。
- 关键点:不可见节点不进渲染树。比如
display: none的元素、<head>里的标签、<meta>等都被排除。注意visibility: hidden的元素仍在渲染树里(它占位置只是不可见),而display: none则完全不占位、不进树。
第五步:布局(Layout / Reflow)

有了渲染树(知道画什么、什么样式),但还不知道每个元素在哪、多大。布局阶段就是算这个。
- 做什么:从渲染树根节点开始遍历,根据盒模型、视口尺寸、定位规则,计算出每个节点的精确几何信息——位置坐标和宽高尺寸。
- 作用:把相对的、百分比的、自适应的样式,换算成屏幕上的绝对像素位置。
- 关键点:布局结果依赖视口大小。窗口缩放、元素尺寸变化都会触发重新布局,这叫 重排(Reflow),是开销最大的渲染操作之一,因为它可能引发整棵树重新计算。
第六步:绘制(Paint)
布局算出了位置和大小,绘制阶段把这些转成实际的像素。
- 做什么:浏览器把渲染树转成一系列绘制指令(先画背景、再画边框、再画文字……),按顺序执行,把每个元素的颜色、边框、阴影、文字等填充成位图像素。
- 作用:生成元素的可视化内容。
- 关键点:复杂的页面会被拆分成**多个绘制图层(Layer)**分别绘制。只改颜色、背景这类不影响布局的属性,只触发 重绘(Repaint),不需要重排,开销比 Reflow 小。
重排 vs 重绘:改变几何属性(宽高、位置、字号)→ 触发重排 + 重绘;只改外观属性(颜色、背景、阴影)→ 只触发重绘。重排一定带来重绘,重绘不一定带来重排。
第七步:合成(Composite)与上屏
现代浏览器的最后一步,把分层绘制的结果组合成最终画面。
- 做什么:浏览器把页面分成多个合成层,每层独立绘制成位图,再交给 GPU 按正确的层级、位置叠加合成为一帧完整画面,输出到屏幕。
- 作用:利用 GPU 并行能力提升合成效率,尤其对动画、滚动、变换非常关键。
- 关键优化点:
transform、opacity这类属性的变化可以只在合成阶段处理,跳过重排和重绘,由 GPU 直接合成。这就是为什么用transform: translate()做动画比改top/left流畅得多——后者会触发重排,前者只走合成。
完整链路与阻塞关系总结
| 阶段 | 做什么 | 产出 | 常见阻塞 / 性能点 |
|---|---|---|---|
| 1. 解析 HTML | 字节→标记→节点 | DOM 树 | 边下边解析;JS 会中断它 |
| 2. 解析 CSS | 层叠计算样式 | CSSOM 树 | CSS 阻塞渲染,需尽早加载 |
| 3. 执行 JS | 可能改 DOM/读样式 | — | 阻塞解析;用 defer/async 缓解 |
| 4. 合并渲染树 | 可见节点 + 样式 | 渲染树 | display:none 不入树 |
| 5. 布局 Layout | 算位置和尺寸 | 几何盒 | 重排(Reflow),开销最大 |
| 6. 绘制 Paint | 转像素位图 | 各图层位图 | 重绘(Repaint) |
| 7. 合成 Composite | GPU 叠加图层 | 屏幕画面 | transform/opacity 只走这步 |
各阶段是前后依赖的流水线:上一步的产出是下一步的输入。但它并非一次性走完——JS 修改 DOM 或样式后,会从对应阶段重新触发后续流程。改了几何属性,从布局重跑(最贵);只改颜色,从绘制重跑;只改 transform/opacity,只跑合成(最省)。 理解这条链路,就能解释绝大多数前端性能优化的底层依据:减少重排、合理使用合成层、不阻塞关键渲染路径。