从输入URL到页面加载的全过程

开发者面试

总览流程

  1. DNS解析
  2. TCP三次握手
  3. TLS协商(HTTPS)
  4. 发送HTTP请求
  5. 服务器处理并返回HTTP响应码
  6. HTML 解析为 DOM树
  7. CSS 解析为 CSSOM树
  8. 合成渲染树
  9. 布局
  10. 绘制
  11. 合成

1. DNS解析

1.1. 解析步骤

  1. 客户端本地递归解析器(如ISP的DNS)发起递归请求
  2. 递归解析器依次发送迭代查询:
    • 先向根域名服务器查询,根返回顶级域(TLD)服务器(如.com)地址
    • 再向顶级域服务查询,顶级域返回**权威域名服务器(如.baidu.com)地址
    • 最后向权威域名服务器查询,权威返回最终的A(IPv4地址)/AAAA(IPv6地址)记录(IP地址)
  3. 递归解析器将结果返回给客户端,并逐级缓存

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 ExpiresLast-Modified / If-Modified-SinceETag / 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. 前端实践

资源类型推荐策略原因
HTMLCache-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-cacheprivate, max-age=60动态数据短缓存

5.6. 打包配置

// webpack.config.jsmodule.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语法分析tokenNode根据令牌创建对应的节点对象
4树构建NodeDOM树按照嵌套规则将节点挂载到正确位置
<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语法分析tokenCSSRule根据CSS语法规则创建规则对象
4树构建CSSRuleCSSOM树按来源、优先级组织样式规则

8. 合成渲染树

合成渲染树是将DOM树和CSSOM树合并,生成一棵只包含可见节点及其计算后样式的树,用于后续的布局和绘制。

渲染树 = DOM树 + CSSOM树 - 不可见节点
  • 渲染树 = DOM树 + CSSOM树 - 不可见节点
  • ✅ CSSOM树提供样式规则
  • ❌ 过滤掉不可见节点 (display: none <script> meta 等)
  • ✅ 保留可见节点,并附上计算后的最终样式

9. 布局

10. 绘制

11. 合成

12. 整个流程优化建议

一. 网络层

  • DNS预解析,预链接
<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:使用deferasync避免阻塞,非关键模块动态加载
  • HTML:预加载关键资源(preload),扁平化DOM结构

三. 渲染层

  • 布局:批量修改样式、读写分离、使用transform代替top/left
  • 绘制:避免高成本属性(box-shadowfilter),缩小重绘区域
  • 合成:使用will-change提升动画元素为独立图层,避免图层爆炸