这篇文章的起因很朴素:被面试官问到"浏览器输入 URL 后发生了什么",我当时答得磕磕绊绊。事后复盘,发现自己其实每天都在和这条链路打交道,却从没认真梳理过它。所以这篇更多是我的学习笔记------不追求教科书式的完整,而是希望用对话感把每个概念说清楚。如果你也对这条链路模糊,欢迎一起往下读。
一、为什么要理解这条链路?
先说面试:这是前端面试里的"经典送命题"。问法很宽,可以从 DNS 聊到渲染,每个环节都能展开一个小时。
但比面试更重要的是:理解浏览器在帮你做什么。
为什么 <script> 放底部?为什么 transform 比 top 流畅?为什么 HTTPS 比 HTTP 安全?这些问题的答案,都藏在这条链路里。
完整流程如下,我们逐段拆解:
css
URL 输入
↓
DNS 解析(域名 → IP)
↓
TCP 三次握手(建立连接)
↓
TLS 握手(HTTPS 加密,若有)
↓
HTTP 请求 / 响应
↓
浏览器解析渲染(HTML → 像素)
↓
页面显示
二、DNS 解析:找到服务器地址
域名是个"电话簿"
你输入的是 www.example.com,但网络层面真正认的是 IP 地址(比如 93.184.216.34)。域名只是给人看的别名。
DNS(Domain Name System)就是把域名翻译成 IP 的"电话簿"。
查询顺序:从近到远
浏览器不会每次都跑去问根服务器,它有一套缓存优先的查询链:
浏览器缓存
↓(没有?)
操作系统缓存(hosts 文件 / 系统 DNS 缓存)
↓(没有?)
路由器缓存
↓(没有?)
ISP(运营商)DNS 服务器
↓(没有?)
根域名服务器 → 顶级域服务器(.com)→ 权威域名服务器
↓
返回 IP 地址,逐层缓存
类比一下:你想找某个老同学的电话,你会先翻自己的手机通讯录,再问共同朋友,最后才去翻毕业纪念册。每一层都比下一层"近"。
TTL:为什么不能永久缓存?
DNS 记录带有 TTL(Time To Live,缓存有效期),过期后必须重新查询。
原因很简单:映射关系会变。比如网站迁移服务器,IP 换了,如果客户端永久缓存旧 IP,就再也找不到新服务器了。TTL 的存在,是在"缓存命中率"和"数据新鲜度"之间做权衡。
三、TCP 三次握手:建立可靠连接
找到 IP 之后,浏览器需要和服务器建立连接。HTTP 跑在 TCP 之上,而 TCP 是面向连接的协议------发数据之前,双方必须先"握手"确认线路通畅。
为什么是三次,不是两次或四次?
这是个很好的问题。我的理解是,三次握手需要确认三件事:
| 次序 | 方向 | 目的 |
|---|---|---|
| 第一次 | 客户端 → 服务器(SYN) | 确认:客户端能发 |
| 第二次 | 服务器 → 客户端(SYN+ACK) | 确认:服务器能收、能发 |
| 第三次 | 客户端 → 服务器(ACK) | 确认:客户端能收 |
三次之后,双方都知道对方能收能发,通信信道建立完毕。
少一次(两次握手)的问题:客户端能收这件事没人确认,存在单向通道风险,且会引发"历史连接"问题(旧的延迟 SYN 包触发服务器建立无效连接)。
多一次没必要:四次就是冗余了,三次已经能确认所有需要确认的状态。
类比:打电话前的确认------"喂,你能听到我吗?"→"能,你能听到我吗?"→"能"。三句话,线路通畅,开始正式通话。
四、TLS 握手:加密 + 身份验证(HTTPS)
TCP 建好连接后,如果是 HTTPS,还要多一步:TLS 握手。
为什么需要它?
HTTP 是明文传输的。你发出去的每一个请求,路径上的任何节点(路由器、运营商、同一 WiFi 下的其他人)理论上都能看到完整内容。用户密码、信用卡号......全部裸奔。
TLS 解决了两个问题:
- 身份验证 :你连接的是真的
example.com,不是被人劫持的钓鱼站 - 加密传输:内容只有你和服务器能读
握手流程(简化版)
markdown
1. 浏览器 → 服务器:我支持这些加密算法 [列表],给我你的证书
2. 服务器 → 浏览器:用这个算法,这是我的证书(含公钥)
3. 浏览器验证证书(向 CA 机构核实真实性)
生成随机数,用服务器公钥加密后发过去
4. 服务器用私钥解密,得到随机数
5. 双方用这个随机数生成"会话密钥"(对称密钥)
6. 后续所有通信用会话密钥加密
两个角色分开理解
初学时我一直搞混"证书"和"加密",其实它们是两件事:
| 角色 | 类比 | 作用 |
|---|---|---|
| 证书 | 身份证 + 公证处盖章 | 证明"我真的是 example.com" |
| 加密 | 双方约定的暗语本 | 保证通信内容只有双方能读 |
证书由 CA(证书颁发机构)签发,浏览器内置了受信任的 CA 列表。如果证书是自签名的或已过期,浏览器会弹出警告。
为什么不全程用公钥加密?
这是个常被忽略的细节。非对称加密(RSA)安全,但比对称加密(AES)慢约 100 倍。
所以 TLS 的设计是:非对称加密只用于握手阶段安全交换密钥,真正的通信内容用对称密钥(AES)加密。兼顾了安全性和性能。
| 加密类型 | 代表算法 | 速度 | 用途 |
|---|---|---|---|
| 非对称加密 | RSA、ECDH | 慢 | 密钥交换、签名 |
| 对称加密 | AES | 快 | 实际数据加密 |
TLS 管加密,Cookie 管身份
还有一个常见混淆点:TLS 建立的是加密信道,不等于"记住了你是谁"。
服务器如何区分不同用户?那是 HTTP 层面 Cookie / session_id 的事。TLS 每次连接都会重新握手(虽然有会话恢复机制),但识别"这个请求属于哪个用户",靠的是请求头里的 Cookie。
五、HTTP 请求与响应
握手完成,浏览器发出第一个 HTTP 请求:
javascript
GET /index.html HTTP/1.1
Host: www.example.com
User-Agent: Mozilla/5.0 ...
Accept: text/html
Cookie: session_id=abc123
Cache-Control: no-cache
几个重要的请求头:
Host:告诉服务器你访问的是哪个域名(一台服务器可能托管多个域名)Cookie:带上本地存储的会话标识Cache-Control:告诉服务器/中间缓存怎么处理这个请求的缓存
服务器返回响应:
javascript
HTTP/1.1 200 OK
Content-Type: text/html; charset=UTF-8
Cache-Control: max-age=3600
<!DOCTYPE html>...
常见状态码速查:
| 状态码 | 含义 | 常见场景 |
|---|---|---|
| 200 | 成功 | 正常响应 |
| 301 | 永久重定向 | 域名迁移 |
| 304 | 内容未修改 | 使用本地缓存 |
| 404 | 资源不存在 | 路径错误 |
| 500 | 服务器内部错误 | 后端异常 |
六、浏览器解析:从 HTML 到像素
拿到 HTML 之后,浏览器开始做"最后一公里"的工作:把代码变成屏幕上的像素。这个过程叫关键渲染路径(Critical Rendering Path) 。
Step 1:解析 HTML → DOM 树
浏览器从上到下解析 HTML,构建 DOM(Document Object Model)树。DOM 是页面结构的内存表示,每个标签对应一个节点。
html
<!-- 这段 HTML -->
<body>
<div class="container">
<p>Hello</p>
</div>
</body>
<!-- 对应的 DOM 树(简化) -->
body
└── div.container
└── p
└── "Hello"
Step 2:下载并解析 CSS → CSSOM 树
并行下载 CSS 文件,解析生成 CSSOM(CSS Object Model)树。结构和 DOM 类似,但存的是样式信息。
关键阻塞规则
这里有个绕不开的问题,很多性能优化都源于此:
| 资源类型 | 阻塞什么 | 原因 |
|---|---|---|
| CSS | 阻塞渲染 | 没 CSSOM 就没法确定元素最终样式 |
| JS(无属性) | 阻塞HTML 解析 | JS 可能操作 DOM,所以得等 JS 执行完 |
JS(defer) |
不阻塞解析 | 延迟到 HTML 解析完才执行 |
JS(async) |
下载不阻塞,执行阻塞 | 下载完立刻执行 |
这就是为什么 <script> 推荐放在 </body> 前,或者使用 defer:避免阻塞 HTML 解析,提升首屏速度。
Step 3:DOM + CSSOM → Render Tree
合并 DOM 和 CSSOM,生成只包含可见节点的 Render Tree(渲染树)。
注意:display: none 的元素不进入 Render Tree(它不占空间、不显示);但 visibility: hidden 的元素会进入(它仍然占位)。
Step 4:Layout(重排 / Reflow)
基于 Render Tree,计算每个节点的精确位置和尺寸------相对视口的坐标、宽高、边距......
这步代价较高。任何改变元素几何属性的操作(改 width、margin、position)都会触发 Reflow,浏览器需要重新计算布局。
Step 5:Paint(重绘 / Repaint)
按照布局结果,把每个元素"画"出来:填充颜色、绘制边框、阴影、文字......
Step 6:Composite(合成)
浏览器把不同图层合并,最终送到屏幕显示。
这里有个重要的性能优化点:
css
/* 只触发 Composite,性能最好 */
.card {
transform: translateY(-4px);
opacity: 0.9;
}
/* 触发 Layout + Paint + Composite,代价最高 */
.card {
top: -4px; /* 改变几何属性 */
}
transform 和 opacity 的变化不影响布局,浏览器可以直接在 GPU 层面处理,跳过 Layout 和 Paint,性能最优。这就是为什么 CSS 动画推荐优先用 transform。
七、整条链路总结
css
用户输入 URL
│
▼
┌──────────────────┐
│ DNS 解析 │ 域名 → IP(电话簿查询,逐层缓存)
└──────────────────┘
│
▼
┌──────────────────┐
│ TCP 三次握手 │ 确认双方能收发(SYN → SYN+ACK → ACK)
└──────────────────┘
│
▼
┌──────────────────┐
│ TLS 握手(HTTPS)│ 证书验证 + 交换会话密钥(非对称→对称)
└──────────────────┘
│
▼
┌──────────────────┐
│ HTTP 请求/响应 │ 发送 Request,接收 HTML/CSS/JS
└──────────────────┘
│
▼
┌──────────────────────────────────────────┐
│ 浏览器渲染流水线 │
│ HTML → DOM ┐ │
│ ├→ Render Tree → Layout │
│ CSS → CSSOM ┘ → Paint → 合成 │
└──────────────────────────────────────────┘
│
▼
页面显示 🎉
延伸与发散
在梳理这条链路的过程中,我产生了一些新的疑问,记录在这里:
- HTTP/2 和 HTTP/3 对这条链路的影响是什么? HTTP/2 的多路复用是不是意味着 TCP 握手的成本被摊薄了?HTTP/3 基于 UDP 的 QUIC 协议又是如何处理可靠性的?
- Service Worker 如何介入这条链路? PWA 的离线缓存是在哪个环节"截胡"请求的?
- 浏览器的预加载机制 (
<link rel="preconnect">、<link rel="prefetch">)是在提前做哪几步?
这些可能会是后续文章的方向,也欢迎有经验的朋友交流。
🧠 面试常问版(核心记忆点)
如果只有 5 分钟时间,记住这 5 条:
- DNS:域名→IP,查询链是浏览器缓存→OS→路由→ISP→根服务器,TTL 控制缓存时效。
- TCP 三次握手:确认双方能收发,三次刚好,少一次有安全隐患,多一次冗余。
- TLS:证书验证身份,非对称加密只用于交换密钥,实际内容用 AES(对称)加密,快 100 倍。
- 渲染阻塞 :CSS 阻塞渲染,JS 阻塞 HTML 解析,所以
<script>放底部或用defer。 - 渲染性能 :
transform/opacity只触发合成层,跳过 Layout 和 Paint,动画优先使用。
参考资料
- MDN - How browsers work - 浏览器工作原理官方文档
- MDN - DNS - DNS 基础概念
- MDN - TLS - TLS 协议说明
- web.dev - Critical Rendering Path - 关键渲染路径
- web.dev - Rendering Performance - CSS 属性与渲染性能