浏览器从输入到绘制 UI 的过程
2026年3月24日大约 3 分钟
浏览器从输入到绘制 UI 的过程
背景
浏览器将一个 URL 渲染成可交互页面,本质上是两条流水线协同完成:
- 网络流水线:把资源可靠、尽快地取回来。
- 渲染流水线:把资源解析成可见像素并交给 GPU 合成显示。
典型路径可以抽象为:
URL 输入 -> 请求发起 -> 响应返回 -> 解析构建 -> 布局绘制 -> 合成显示
网络阶段
1. URL 处理与缓存命中
浏览器在真正发请求前,会做一轮本地决策:
- 解析 URL(协议、域名、端口、路径)。
- 检查强缓存/协商缓存是否可直接命中。
- 判断是否可复用已有连接(HTTP/2 多路复用、Keep-Alive)。
这一步决定了是否需要进入完整网络链路。
2. DNS 解析
DNS 的目标是把域名映射为 IP。常见查询路径:
- 浏览器 DNS 缓存。
- 操作系统缓存。
- 本地域名服务器。
- 递归/迭代到权威 DNS。
需要关注的工程问题:
- 缓存命中过期 IP 可能导致短暂访问失败。
- TTL 设计过长会降低切流/故障切换速度。
3. 连接建立(TCP/TLS)
在 HTTP 请求前,需要建立传输通道:
- TCP 三次握手建立可靠连接。
- HTTPS 场景下继续完成 TLS 握手与密钥协商。
断开连接通常由 TCP 四次挥手完成。
4. HTTP 请求与响应
连接建立后,浏览器发送请求报文,服务端返回响应头和响应体。资源可能分块传输,浏览器按协议顺序重组。
渲染构建阶段
1. 解析 HTML,构建 DOM
主线程解析 HTML token 并构建 DOM 树。过程中会触发预加载扫描器(preload scanner)提前发现外链资源。
2. 解析 CSS,构建 CSSOM
CSS 会被解析为 CSSOM。DOM 与 CSSOM 都准备好后,浏览器才能生成 Render Tree。
3. JavaScript 执行对解析的影响
默认 <script> 会阻塞 HTML 解析;defer 和 async 的语义不同:
| 类型 | 下载时机 | 执行时机 | 是否阻塞 HTML 解析 | 执行顺序 |
|---|---|---|---|---|
<script> | 解析到标签时下载 | 下载完成立即执行 | 是 | 按文档顺序 |
<script defer> | 异步下载 | DOM 解析完成后执行 | 否 | 按文档顺序 |
<script async> | 异步下载 | 下载完成立即执行 | 否 | 不保证顺序 |
实践中,业务脚本优先 defer,统计/广告类第三方脚本优先 async。
4. Render Tree、Layout、Paint、Composite
渲染流水线核心阶段:
- Render Tree:合并 DOM 与 CSSOM,仅保留可见节点。
- Layout(回流):计算元素几何信息(位置与尺寸)。
- Paint(重绘):把视觉样式绘制到不同图层。
- Composite(合成):GPU 合成图层并显示到屏幕。
常见性能瓶颈
- 阻塞脚本过多,导致 DOM 构建与首屏渲染推迟。
- 大体积 CSS/JS 未拆分,关键路径资源过重。
- 高频读写布局属性(如
offsetWidth)导致强制同步布局。 - 非必要动画触发
layout/paint,而不是仅在合成层完成。
工程最佳实践
- 首屏关键资源前置:
preload、preconnect、关键 CSS 内联。 - 脚本策略优化:业务脚本
defer,第三方脚本async。 - 减少主线程长任务:代码分包、懒加载、Web Worker 下放计算。
- 降低渲染成本:优先使用
transform/opacity做动画。 - 用指标驱动优化:持续观测
LCP、INP、CLS与 Long Task。
总结
从输入 URL 到页面可见,不是一个“网络请求完成就结束”的过程,而是“网络获取 + 渲染执行”两条链路的协同。前端性能优化的重点在于缩短关键路径、避免主线程阻塞、减少高成本渲染阶段的重复触发。
