Skip to content

从 URL 到页面渲染完整流程

📚 相关文档

一、整体流程概览

``mermaid graph TD A[用户输入 URL] --> B[URL 解析] B --> C[DNS 解析] C --> D[TCP 握手] D --> E{HTTPS?} E -->|是| F[TLS 握手] E -->|否| G[发送 HTTP 请求] F --> G G --> H[服务器响应] H --> I[解析 HTML → DOM] I --> J[解析 CSS → CSSOM] J --> K[生成 Render Tree] K --> L[布局 Layout] L --> M[绘制 Paint] M --> N[合成 Composite] N --> O[屏幕显示]

style A fill:#e1f5ff
style O fill:#d4edda
style F fill:#fff3cd

**时间分配参考**:
- DNS + TCP + TLS:~100-300ms
- 服务器响应:~50-500ms
- **前端渲染:~100-2000ms**(优化重点)

---

## 二、网络阶段(简述)

::: info 💡 详细说明请查看专项文档
以下阶段在独立文档中深入讲解,此处仅概述流程。
:::

### 1. DNS 解析
将域名转换为 IP 地址,经过多级缓存(浏览器 → 系统 → ISP → 权威 DNS)。

👉 详细原理:[DNS 域名解析原理与优化](./03.DNS 域名解析原理与优化.md)

### 2. TCP 握手
建立可靠连接(三次握手),确保双方收发能力正常。

👉 详细原理:[TCP 三次握手与四次挥手](./04.TCP 三次握手与四次挥手.md)

### 3. TLS 握手(HTTPS)
- **TLS 1.2**:2-RTT
- **TLS 1.3**:1-RTT(优化)
- **0-RTT**:会话恢复模式

### 4. HTTP 请求与响应

**请求报文**:
```http
GET /path HTTP/1.1
Host: www.example.com
User-Agent: Mozilla/5.0...
Accept: text/html
Cookie: session_id=abc123

响应报文

http
HTTP/1.1 200 OK
Content-Type: text/html; charset=utf-8
Cache-Control: max-age=3600
ETag: "abc123"

<!DOCTYPE html>...

三、渲染阶段(核心)

1. 关键渲染路径(CRP)

``mermaid graph TB A[HTML] --> B[构建 DOM 树] C[CSS] --> D[构建 CSSOM 树] B --> E[合并生成 Render Tree] D --> E E --> F[布局 Layout] F --> G[绘制 Paint] G --> H[合成 Composite] H --> I[栅格化 Rasterize] I --> J[GPU 上屏]

style A fill:#ffe6e6
style C fill:#e6f3ff
style J fill:#d4edda

### 2. 构建 DOM 树

**特点**:
- ✅ **增量构建**:边下载边解析
- ✅ **预加载扫描器**:遇到 `<img>`、`<link>` 提前发起请求
- ⚠️ **阻塞行为**:`<script>` 会阻塞解析(除非 `async/defer`)

```html
<!DOCTYPE html>
<html>
  <head>
    <link rel="stylesheet" href="style.css"> <!-- 不阻塞解析,但阻塞渲染 -->
  </head>
  <body>
    <div class="container">
      <h1>Hello</h1>
    </div>
    <script src="app.js"></script> <!-- 阻塞解析 -->
  </body>
</html>

3. 构建 CSSOM 树

特性

  • 层叠性:多个规则按优先级合并
  • 继承性colorfont-size 等从父元素继承
  • 计算值:相对单位(em%)转为绝对单位(px

4. 生成 Render Tree

合并 DOM + CSSOM,排除不可见元素

排除

  • <script><meta><link>
  • display: none 的元素

保留

  • visibility: hidden(仍占空间)
  • opacity: 0(仍参与渲染)

5. 布局(Layout / Reflow)

计算每个元素的精确位置和尺寸

触发布局的操作

操作说明
首次加载必然触发
窗口大小变化重新计算
修改 width/height/margin影响几何属性
添加/删除 DOM 节点影响周围元素
读取 offsetWidth强制同步布局

性能优化

  • ✅ 批量修改样式,避免多次重排
  • ✅ 使用 transform 替代 top/left(不触发布局)
  • ❌ 避免"布局抖动"(读写交替)

6. 绘制(Paint)

将渲染树转换为实际像素

过程

  1. 分层:划分为多个图层(Layer)
  2. 生成绘制指令:为每个图层生成命令
  3. 光栅化:矢量图形 → 位图

触发绘制的操作

  • 修改颜色、背景、边框、阴影
  • 添加/删除元素

优化

  • 减少复杂效果(渐变、模糊)
  • 使用 will-change 提示浏览器

7. 合成(Composite)

将多个图层叠加形成最终画面。

优势

  • 独立更新:某图层变化只需重新合成该层
  • GPU 加速:利用并行计算
  • 动画流畅transform/opacity 跳过布局和绘制

触发合成的操作

  • transform(平移、旋转、缩放)
  • opacity(透明度)

性能优化

  • ✅ 动画使用 transform/opacity(保证 60 FPS)
  • position: fixed/sticky 创建独立图层
  • ✅ 合理使用 will-change: transform

四、性能优化关键点

1. 首屏优化实战

① 关键资源优先级

html
<!-- 高优先级 -->
<link rel="preload" href="critical.css" as="style">
<link rel="preload" href="hero-image.webp" as="image">

<!-- 低优先级 -->
<link rel="prefetch" href="analytics.js" as="script">

② 内联关键 CSS

html
<head>
  <style>
    /* Critical CSS: Header + Hero */
    header { height: 60px; }
    .hero { display: flex; }
  </style>
  <link rel="stylesheet" href="non-critical.css" media="print" onload="this.media='all'">
</head>

③ 延迟加载 JS

html
<script src="app.js" defer></script>
<script src="analytics.js" async></script>

④ 图片优化

html
<img 
  src="image-800w.webp" 
  srcset="image-400w.webp 400w, image-800w.webp 800w"
  loading="lazy"
  decoding="async"
>

2. 性能监控指标

指标全称优秀标准工具
FCPFirst Contentful Paint< 1.8sLighthouse
LCPLargest Contentful Paint< 2.5sWeb Vitals
FIDFirst Input Delay< 100msPerformance API
CLSCumulative Layout Shift< 0.1Web Vitals

五、面试标准回答

从输入 URL 到页面渲染主要经历:

  1. 网络阶段:DNS 解析 → TCP 握手 → TLS 握手(HTTPS)→ 发送 HTTP 请求 → 接收响应
  2. 渲染阶段:解析 HTML 构建 DOM → 解析 CSS 构建 CSSOM → 合并生成 Render Tree → 布局(Layout)→ 绘制(Paint)→ 合成(Composite)→ 上屏显示

前端优化重点在渲染阶段:

  • 提取关键 CSS 内联,异步加载非关键 CSS
  • JS 使用 defer/async 避免阻塞解析
  • 动画使用 transform/opacity 跳过布局和绘制
  • 图片懒加载、WebP 格式、响应式图片

六、高频追问

Q1: 什么是重排(Reflow)和重绘(Repaint)?

  • 重排:元素几何属性改变,需重新计算布局(成本高)
  • 重绘:视觉属性改变,只需重新绘制像素(成本低)
  • 关系:重排必触发重绘,重绘不一定触发重排

避免策略

  • 使用 transform 替代 top/left
  • 批量修改样式
  • 动画元素设置 position: absolute/fixed

Q2: 为什么 <script> 放在 <body> 底部?

  • <script> 会阻塞 HTML 解析
  • 放底部确保 DOM 基本构建完成,避免白屏
  • 现代方案:使用 defer(DOM 解析完后执行)或 async(下载完立即执行)

Q3: 如何优化首屏加载速度?

  • 减少请求:合并、雪碧图、缓存
  • 减小体积:Gzip/Brotli 压缩、Tree Shaking
  • 加快速度:CDN、HTTP/2、预加载
  • 优化渲染:关键 CSS 内联、JS 延迟、transform 动画

七、总结记忆口诀

📝 URL 解析查安全
🔍 DNS 寻址找 IP
🤝 TCP 握手建连接
🔐 TLS 协商加密钥
📤 发送请求等响应
🌳 构建 DOM 和 CSSOM
🎨 渲染树上布局绘
✨ 合成栅格显屏幕

核心优化思路

  • 减少请求、减小体积、加快速度、优化渲染
最近更新