总览流程
- DNS解析
- TCP三次握手
- TLS协商(HTTPS)
- 发送HTTP请求
- 服务器处理并返回HTTP响应码
- HTML 解析为 DOM树
- CSS 解析为 CSSOM树
- 合成渲染树
- 布局
- 绘制
- 合成
1. DNS解析
1.1. 解析步骤
- 客户端向本地递归解析器(如ISP的DNS)发起递归请求
- 递归解析器依次发送迭代查询:
- 先向根域名服务器查询,根返回顶级域(TLD)服务器(如
.com)地址
- 再向顶级域服务查询,顶级域返回**权威域名服务器(如
.baidu.com)地址
- 最后向权威域名服务器查询,权威返回最终的A(IPv4地址)/AAAA(IPv6地址)记录(IP地址)
- 递归解析器将结果返回给客户端,并逐级缓存
1.2. 缓存层级
| 层级 | 说明 | 是否缓存 |
|---|
| 客户端(浏览器/电脑) | 浏览器和操作系统都有DNS缓存 | ✅ 是 |
| 递归解析器(如114.114.114.114) | 最重要的缓存层,缓存结果下次直接返回,不用再问根->顶级->权威 | ✅ 是 |
| 根域名服务器 | 只返回顶级域名列表,不缓存具体域名 | ❌ 否 |
| 顶级域服务 | 只返回二级域的权威服务器地址,不缓存具体域名 | ❌ 否 |
| 权威域名服务器 | 数据源头,可能临时缓存减少负载 | ✅ 可能 |
1.3. 笔试回答
DNS解析是将域名转化为IP地址的过程,采用递归+迭代的方式,1.客户端向本地递归解析器发起递归查询 -> 递归解析器依次向根域名服务器/顶级域服务器/权威域名服务器发起迭代查询 -> 根服务返回顶级域名地址,顶级域服务器返回权威域名地址,权威域名服务器返回最终 A/AAAA记录 -> 递归服务器将IP返回给客户端,各层缓存结果
2. TCP链接
三次握手建立可靠连接的过程,通过三次报文交换确认双发的收发能力正常,并同步初始序列号
2.1 完整流程
| 次数 | 发送方 -> 接收方 | 报文信息 | 作用 |
|---|
| 第一次 | 客户端 -> 服务端 | SYN=1, seq=x | 客户端请求建立连接,告知序列号x |
| 第二次 | 服务端 -> 客户端 | SYN=1, ACK=1, seq=y, ack=x+1 | 服务端收到客户端的SYN,同时发送自己的SYN |
| 第三次 | 客户端 -> 服务端 | ACK=1, seq=x+1, ack=y+1 | 客户端确认接受服务器的SYN - ACK,连接建立 |
其中 seq 为发送方序列号,ack 为期望收到的下一次序列号(对方的seq + 1),序列号生成是操作系统根据时间、随机数、哈希算法 等生成的,通常是一个单调递增(但会回绕)的32位无符号整数,目的是防止网络中的旧报文干扰新连接。
SYN表示同步(告诉对方我来了),ACK表示确认(表示我接收到了),1为布尔值
3. TLS协商
TLS协商(TLS握手)是客户端和服务器在TCP连接之上,协商加密参数、验证身份、生成会话密钥的过程,最终建立安全的加密通道。
4. 发送HTTP请求
HTTP(超文本传输协议)是一种无状态的应用层协议,基于请求-响应模式,客户端向服务器发送HTTP请求报文,服务器返回HTTP响应报文。
4.1. 请求报文结构
【请求行】 GET /index.html HTTP/1.1
【请求头】 Host: www.example.com
User-Agent: Chrome/120.0
Accept: text/html
【空行】
【请求体】 username=zhang&password=123(POST时才有)
| 组成部分 | 作用 | 示例 |
|---|
| 请求行 | 方法 + url + 版本 | GET /api/user HTTP/1.1 |
| 请求头 | 键值对,描述请求附加信息 | Host User-Agent Cookie |
| 空行 | 分隔头部和体部 | \r\n\r\n |
| 请求体 | 携带的数据(POST / PUT) | {"name": "张三"} |
4.2. 常见请求头
| 请求头 | 作用 | 示例 |
|---|
| Host | 服务器域名 | Host: www.baidu.com |
| User-Agent | 标识客户端类型 | User-Agent: Mozilla/5.0 |
| Accept | 期望的响应内容类型 | Accept: application/json |
| Content-Type | 请求体格式 | Content-Type: application/json |
| Content-length | 请求头长度(字节) | Content-length: 25 |
| Cookie | 携带会话信息 | Cookie: sessionId=abc123 |
| Authorization | 身份认证令牌 | Authorization: bearer xxxxx |
| Referer | 请求来源页面 | Referer: Https://google.com |
| Origin | 请求源(CORS用) | Origin: https://example.com |
5. 服务器处理并返回HTTP响应码
5.1. 响应报文
【响应行】 HTTP/1.1 200 OK
【响应头】 Content-Type: text/html
Content-length: 1234
Set-Cookie: sessionId=xyz
【空行】
【响应体】 <html><body>...</body></html>
| 组成部分 | 作用 | 示例 |
|---|
| 状态行 | 版本 + 状态码 + 状态描述 | HTTP/1.1 200 OK |
| 响应头 | 描述响应的附加信息 | Content-Type Set-Cookie |
| 空行 | 分隔头部和体部 | \r\n\r\n |
| 响应体 | 返回的实际数据 | HTML,JSON,图片等 |
5.2. HTTP状态码
| 分类 | 含义 | 典型状态码 |
|---|
| 1XX | 信息性 | 100 Continue (继续发送) |
| 2XX | 成功 | 200 OK 201 Created 204 No Content |
| 3XX | 重定向 | 301 Moved Permanently 302 Found 304 Not Modified |
| 4XX | 客户端失败 | 400 Bad Request 401 Unauthorized 403 Forbidden 404 Not Found |
| 5XX | 服务端失败 | 500 Internal Server Error 502 Bad Gateway 503 Service Unavailable |
5.3. HTTP缓存
| 维度 | 强缓存 | 协商缓存 |
|---|
| 判断依据 | 本地时间 vs 过期时间 | 向服务器验证资源是否修改 |
| 是否发出请求 | ❌ 不发 | ✅ 发(但可能无 body) |
| 响应状态码 | 200 (from cache) | 304 Not Modified |
| 控制头 | Cache-Control Expires | Last-Modified / If-Modified-Since,ETag / If-None-Match |
| 优先级 | 高(命中则直接使用) | 低(强缓存失效后触发) |
┌─────────────────┐│ 发起请求 │└────────┬────────┘ ↓┌─────────────────┐│ 是否有缓存? │──No──→ 请求服务器 ──→ 200 + 缓存响应└────────┬────────┘ ↓ Yes┌─────────────────┐│ 强缓存是否有效? │──Yes─→ 使用缓存 (200 from cache)└────────┬────────┘ ↓ No(过期)┌─────────────────┐│ 发送协商请求 ││ (If-Modified... ││ If-None-Match)│└────────┬────────┘ ↓┌─────────────────┐│ 服务器判断 │└──┬──────┬───────┘ ↓ 未修改 ↓ 已修改 304 200 + 新资源 无body 有body 读缓存 更新缓存
5.4. Cache-Control
| 指令 | 含义 | 示例 |
|---|
max-age=<seconds> | 相对过期时间 | max-age=3600 |
no-cache | 不使用强缓存,每次都发起请求验证 | no-cache |
no-store | 完全不缓存(既不存也不读) | no-store |
public | 允许任意中间节点(CDN, 代理)缓存 | public, max-age=3600 |
private | 仅浏览器缓存,不允许中间节点 | private, max-age=3600 |
must-revalidate | 过期后必须验证,不能使用过期资源 | max-age=4600, must-revalidate |
s-maxage | 针对CDN/代理的过期时间 | s-maxage=3600 |
5.5. 前端实践
| 资源类型 | 推荐策略 | 原因 |
|---|
| HTML | Cache-Control: no-cache | 每次验证,保证最新 |
| ** CSS/JS(带hash) | Cache-Control: public, max-age=3153600, immutable | 文件名变化即新资源,可长期缓存 |
| CSS/JS(不带hash) | Cache-Control: no-cache | 避免旧版本被缓存 |
| 图片/字体 | Cache-Control: public, max-age=86400 | 缓存一天,或更长 |
| API 数据 | 按需: no-cache 或 private, max-age=60 | 动态数据短缓存 |
5.6. 打包配置
module.export = { output: { fileNmae: '[name].[contenthash:8].js', chunkFilename: '[name].[contenthash:8].chunk.js', }}
5.7. nginx配置
实际开发中好像很少在业务代码中看到设置缓存机制,分层处理,统一在网关 nginx 设置,应用层只设置一些特殊情况下的缓存
看这里 👉 nginx
6. HTML解析为DOM树
将HTML文档转换为DOM树的过程,浏览器以此构建网页的结构化表示,供JavaScript操作和渲染引擎使用。
步骤
| 步骤 | 名称 | 输入 → 输出 | 说明 |
|---|
| 1 | 字节解码 | 二进制 → 字符串 | 根据 Content-Type 或 meta charset解码 |
| 2 | 语法分析 | 字符串 → token | 将字符拆分成开始标签,结束标签,属性,文本等令牌 |
| 3 | 语法分析 | token → Node | 根据令牌创建对应的节点对象 |
| 4 | 树构建 | Node → DOM树 | 按照嵌套规则将节点挂载到正确位置 |
<p class="text">Hello</p>
1. 开始标签: p, 属性: class="text"
2. 文本节点: "Hello"
3. 结束标签: p
解析过程中的阻塞与优化
1. CSS阻塞
| 情况 | 是否阻塞DOM解析 | 是否阻塞渲染 |
|---|
| CSS下载/解析 | ❌ 不阻塞DOM解析 | ✅ 阻塞渲染 |
| 内联CSS | ❌ 不阻塞DOM解析 | ✅ 阻塞渲染 |
原因: CSS可能影响DOM样式,浏览器需要等待CSS解析完成后才渲染,避免出现无样式内容闪烁
2. Javascript阻塞
| 脚本类型 | 是否阻塞DOM解析 | 说明 |
|---|
<script> | ✅ 阻塞 | 立即下载并执行,暂停DOM解析 |
<script async> | ❌ 不阻塞 | 异步下载,下载完立即执行(可能阻塞) |
<script defer> | ❌ 不阻塞 | 异步下载,DOM解析完成后执行 |
<script type="module"> | ❌ 不阻塞 | 默认defer行为 |
3. 优化策略
| 策略 | 说明 |
|---|
| CSS放头部 | <link>放在 <head>, 尽早下载解析 |
| JS放在底部 | <script>放在<body>前,避免阻塞 |
| async/defer | 非关键脚本使用异步加载 |
| 关键内联 | 首屏关键CSS/JS内联,减少请求 |
| 预加载 | <link rel="preload">提前加载资源 |
重排与重绘
| 操作 | 触发条件 | 开销 |
|---|
| 重排 | 改变几何属性 (宽高、边框、位置) | 高 |
| 重绘 | 改变视觉属性(颜色,背景图) | 中 |
| 不触发 | 改变transform opacity | 低 |
7. CSS 解析成 CSSOM树
CSS解析是将CSS样式表转换为CSSOM树的过程,CSSOM树与DOM树合并后形成渲染树,用于页面的布局和绘制。
步骤
| 步骤 | 名称 | 输入 → 输出 | 说明 |
|---|
| 1 | 字节解码 | 二进制 → 字符串 | 根据 Content-Type 或 meta charset解码 |
| 2 | 语法分析 | 字符串 → token | 拆分为IDENT、HASH、COLON、SEMICOLON等令牌 |
| 3 | 语法分析 | token → CSSRule | 根据CSS语法规则创建规则对象 |
| 4 | 树构建 | CSSRule → CSSOM树 | 按来源、优先级组织样式规则 |
8. 合成渲染树
合成渲染树是将DOM树和CSSOM树合并,生成一棵只包含可见节点及其计算后样式的树,用于后续的布局和绘制。
渲染树 = DOM树 + CSSOM树 - 不可见节点
- 渲染树 = DOM树 + CSSOM树 - 不可见节点
- ✅ CSSOM树提供样式规则
- ❌ 过滤掉不可见节点 (
display: none <script> meta 等)
- ✅ 保留可见节点,并附上计算后的最终样式
9. 布局
10. 绘制
11. 合成
12. 整个流程优化建议
一. 网络层
<link rel="dns-prefetch" href="//api.example.com">
<link rel="preconnect" href="//cdn.example.com">
- 设置合理缓存策略(Cache-Control)
- 启用HTTP/2、TLS 1.3、Gzip/Brotli 压缩
二. 资源加载
- CSS:关键CSS内联,非关键CSS延迟加载
- JS:使用
defer或async避免阻塞,非关键模块动态加载
- HTML:预加载关键资源(
preload),扁平化DOM结构
三. 渲染层
- 布局:批量修改样式、读写分离、使用
transform代替top/left
- 绘制:避免高成本属性(
box-shadow、filter),缩小重绘区域
- 合成:使用
will-change提升动画元素为独立图层,避免图层爆炸