输入 URL 至页面显示的过程
整体流程
输入 URL
→ 强缓存检查
→ DNS 解析
→ TCP 握手 → TLS 握手(HTTPS)
→ 发送 HTTP 请求 → 协商缓存检查
→ 服务端返回响应
→ 浏览器渲染(DOM → CSSOM → 渲染树 → 布局 → 绘制)
一、网络阶段
1. 解析 URL
- 合法 URL → 补全协议/路径,发起请求
- 非法 URL → 调用默认搜索引擎搜索
2. 检查强缓存
⚠️ 强缓存在 DNS 之前检查,命中则直接返回,不发任何网络请求
| 字段 | 版本 | 说明 |
|---|---|---|
Expires | HTTP 1.0 | 绝对时间,依赖客户端时钟,可能不准 |
Cache-Control: max-age=N | HTTP 1.1 | 相对秒数,优先级更高 |
- 命中 → 直接读缓存,跳到渲染阶段
- 未命中 → 继续走网络
3. DNS 解析
逐级查找,找到即止:
浏览器 DNS 缓存
→ 操作系统缓存(hosts 文件)
→ 路由器缓存
→ ISP(运营商)DNS 服务器
→ 递归查询根域名服务器 → 顶级域名服务器 → 权威域名服务器
→ 返回 IP 地址
4. 建立 TCP 连接(三次握手)
客户端 ── SYN(x) ──────────────────→ 服务端
客户端 ←── SYN(y) + ACK(x+1) ───────
客户端 ── ACK(y+1) ─────────────────→
5. TLS 握手(HTTPS)
- TLS 1.2:额外 2-RTT
- TLS 1.3:额外 1-RTT(首次连接后可 0-RTT)
6. 发送 HTTP 请求,服务端检查协商缓存
协商缓存在请求到达服务端时检查,需要一次网络请求
| 请求头 | 响应头 | 说明 |
|---|---|---|
If-None-Match | ETag | 内容摘要对比,优先级更高 |
If-Modified-Since | Last-Modified | 时间对比 |
- 未修改 →
304 Not Modified,使用本地缓存 - 已修改 →
200 OK,返回新内容
7. 接收响应,决定 TCP 连接状态
Connection: keep-alive→ 保持连接,复用Connection: close→ 四次挥手,关闭连接
二、渲染阶段
整体顺序
HTML → DOM 树 ──┐
├── 渲染树 → 布局(Layout)→ 绘制(Paint)→ 合成(Composite)
CSS → CSSOM 树 ──┘
1. 构建 DOM 树
字节流 → 字符 → Token → Node → DOM 树(增量构建,边解析边渲染)
2. 构建 CSSOM 树
字节流 → 字符 → Token → CSS 规则 → CSSOM 树
CSS 会阻塞渲染,但不阻塞 HTML 解析(浏览器会继续解析 HTML,但不会渲染)
3. JS 的三种加载方式
<script src="a.js"></script> <!-- 默认:阻塞 HTML 解析,下载 + 执行完才继续 -->
<script src="b.js" async></script> <!-- async:异步下载,下载完立即执行(可能阻塞) -->
<script src="c.js" defer></script> <!-- defer:异步下载,等 HTML 解析完按序执行 -->
默认: ──解析HTML── 停 ──下载JS──执行── 继续解析──
async: ──解析HTML──────────────── 停 ──执行── 继续──
└── 异步下载JS ──────┘
defer: ──解析HTML──────────────────────── 执行defer ──
└── 异步下载JS ─────────────┘ (按序,在 DOMContentLoaded 前)
4. 构建渲染树
- 遍历 DOM 树中所有可见节点(排除
display:none、<head>等) - 为每个可见节点匹配 CSSOM 规则,计算样式
5. 布局(Layout / 回流)
计算每个元素的位置和尺寸,输出"盒模型"。
6. 绘制(Paint / 重绘)
将布局信息转为屏幕上的像素。
7. 合成(Composite)
将多个图层合并,最终显示在屏幕上。
关键事件节点
| 事件 | 触发时机 |
|---|---|
DOMContentLoaded | HTML 解析完成 + defer 脚本执行完,不等图片等资源 |
load | 所有资源(图片、样式、脚本)全部加载完成 |