一次完整页面请求经历的七个步骤
第一步:用户输入 URL 并触发请求
- 这是请求的起点,用户在浏览器地址栏输入 URL (如 https://www.baidu.com) 并按下回车,浏览器会执行前置判断:
- URL 合法性校验:浏览器会检查输入内容是否符合 URL 格式 (协议 + 域名 + 路径),区分是 HTTP/HTTPS 等网络协议,还是 file:// 本地文件协议;
- 缓存优先查询:浏览器会先查询内存缓存 (本次会话的临时缓存) 和磁盘缓存 (长期存储的缓存),如果存在该 URL 的有效缓存 (未过期),则直接使用缓存资源,跳过后续网络请求步骤;
- 触发请求流程:若缓存未命中或缓存过期,浏览器会启动正式的网络请求流程;
第二步:DNS 域名解析:域名 → 服务器 IP 地址
-
网络通信的底层依赖 IP 地址,但用户输入的是便于记忆的域名,因此需要通过 DNS (域名系统) 将域名转换为对应的服务器 IP 地址,解析流程遵循从本地到远程的优先级:
- 浏览器 DNS 缓存:浏览器会缓存近期解析过的域名 - IP 映射,有效期通常为几分钟到几小时 (由 TTL 控制);
- 操作系统 DNS 缓存:若浏览器缓存未命中,会读取操作系统的本地缓存 (如 Windows 的 DNS Client 服务、hosts 文件);
- 本地 DNS 服务器查询:若系统缓存也无记录,浏览器会向本地 DNS 服务器 (通常是路由器或 ISP 提供的 DNS,如 114.114.114.114) 发起请求;
- 递归 / 迭代查询:本地 DNS 服务器若没有该域名记录,会通过递归查询向更上层的 DNS 服务器发起请求:
- 先查询 根 DNS 服务器 (全球共 13 组),获取顶级域 DNS 服务器地址 (如 .com .cn 服务器);
- 再向顶级域 DNS 服务器查询,获取权威 DNS 服务器地址 (域名注册商提供的服务器);
- 最后向权威 DNS 服务器查询,得到目标域名的最终 IP 地址;
-
特殊优化:现代浏览器支持 DNS 预解析 (rel=“dns-prefetch”),可提前解析页面中可能用到的域名,减少后续请求的延迟;
第三步:建立 TCP 连接(三次握手)
-
获取服务器 IP 地址后,浏览器需要与服务器建立 TCP 连接 (HTTP 协议基于 TCP 实现可靠传输),核心是三次握手,确保客户端和服务器的收发能力正常:
- 第一次握手 (SYN):客户端向服务器发送 SYN 报文,报文包含一个随机的初始序列号 (ISN),请求建立连接;此时客户端状态变为 SYN_SENT;
- 第二次握手 (SYN + ACK):服务器收到 SYN 报文后,回复 SYN + ACK 报文;其中 SYN 是服务器的初始序列号,ACK 是对客户端 SYN 报文的确认 (序列号 + 1);此时服务器状态变为 SYN_RCVD;
- 第三次握手 (ACK):客户端收到 SYN + ACK 报文后,回复 ACK 报文,确认服务器的 SYN 报文 (序列号 + 1);此时客户端和服务器状态均变为 ESTABLISHED,TCP 连接正式建立;
-
HTTPS 额外步骤:如果是 HTTPS 协议,TCP 连接建立后会触发 TLS/SSL 握手:验证服务器证书合法性 → 协商加密算法 → 生成会话密钥 → 后续数据传输均为加密状态,防止数据被窃听或篡改;
第四步:发送 HTTP/HTTPS 请求报文
-
TCP 连接建立后,浏览器会构建 HTTP 请求报文 并发送给服务器,请求报文是纯文本格式,由 3 部分组成:
- 请求行:核心信息,格式为 请求方法 + 请求路径 + 协议版本
- 示例:GET /index.html HTTP/1.1
- 常见请求方法:GET (获取资源)、POST (提交数据)、HEAD (仅获取响应头) 等;
- 请求头:键值对格式,携带客户端信息和请求参数,常见字段:
字段 作用 User-Agent 标识浏览器类型、版本、操作系统 Cookie 携带客户端的会话信息 Accept 客户端支持的响应内容类型 (如 text/html) Content-Type 仅 POST 请求,标识请求体的数据格式 (如 application/json) - 请求体:仅 POST/PUT 等方法包含,存放提交的数据 (如表单参数、JSON 字符串),GET 请求的参数会拼接在 URL 后 (如 ?id=1&name=test),无请求体;
- 请求行:核心信息,格式为 请求方法 + 请求路径 + 协议版本
第五步:服务器处理请求并返回响应报文
-
请求接收与解析:服务器的 Web 服务 (如 Nginx、Apache、Node.js 的 Koa/Express) 监听指定端口,接收请求报文后,解析请求行、请求头和请求体,提取关键信息 (请求路径、参数、Cookie 等);
-
资源路由与处理
- 静态资源:如果请求的是 HTML、CSS、JS、图片等静态资源,Web 服务器直接从磁盘读取文件,无需经过应用层处理;
- 动态资源:如果请求的是接口或动态页面 (如 /api/user),Web 服务器会将请求转发给应用服务器 (如 Tomcat、Node.js 应用),应用服务器执行业务逻辑 (如查询数据库、处理数据),生成动态内容;
-
构建响应报文:服务器生成 HTTP 响应报文,同样由 3 部分组成:
- 状态行:格式为 协议版本 + 状态码 + 状态描述;
- 响应头:键值对格式,携带服务器信息和资源属性,常见字段:
字段 作用 Server 标识服务器类型 (如 Nginx/1.21.0) Content-Type 响应体的数据格式 (如 text/html; charset=utf-8) Cache-Control 缓存策略 (如 max-age=3600 表示缓存 1 小时) Set-Cookie 向客户端设置 Cookie 信息 - 响应体:核心内容,即浏览器需要的资源 (HTML 代码、JSON 数据、图片二进制流等);
第六步:关闭 TCP 连接(四次挥手)
-
当请求和响应完成后,TCP 连接会被释放,由于 TCP 是全双工 (双向传输) 协议,关闭连接需要 四次挥手:
- 第一次挥手 (FIN):客户端发送 FIN 报文,告知服务器 “我没有数据要发送了”,客户端状态变为 FIN_WAIT_1;
- 第二次挥手 (ACK):服务器收到 FIN 报文后,回复 ACK 报文,确认客户端的关闭请求,服务器状态变为 CLOSE_WAIT,客户端状态变为 FIN_WAIT_2;
- 第三次挥手 (FIN):服务器处理完剩余数据后,发送 FIN 报文,告知客户端 “我也没有数据要发送了”,服务器状态变为 LAST_ACK;
- 第四次挥手 (ACK):客户端收到 FIN 报文后,回复 ACK 报文,确认服务器的关闭请求,客户端状态变为 TIME_WAIT (等待一段时间,确保服务器收到 ACK),之后连接正式关闭;
-
连接复用优化:HTTP/1.1 支持 Connection: keep-alive 头部,开启后 TCP 连接不会立即关闭,可复用该连接处理后续的资源请求 (如同一页面的 CSS、JS、图片),减少握手开销;HTTP/2 支持多路复用,一个 TCP 连接可同时传输多个请求,进一步提升效率;
第七步:浏览器解析渲染页面
页面请求 7 步骤性能优化清单
步骤 1-2:用户输入 URL + DNS 解析优化(减少域名解析耗时)
-
启用 DNS 缓存:
- 利用浏览器和系统的 DNS 缓存机制,通过设置 DNS 记录的 TTL (生存时间) 参数,延长缓存有效期 (建议设置为 300s-3600s),减少重复查询;
- 注意:TTL 不宜过长,否则域名解析变更会出现延迟生效的问题;
-
DNS 预解析 (dns-prefetch):
- 对页面中即将用到的跨域域名,提前进行 DNS 解析,语法:
<link rel="dns-prefetch" href="https://cdn.example.com"> <link rel="dns-prefetch" href="https://api.example.com"> - 适用场景:页面依赖的 CDN、接口域名、第三方资源域名 (如统计、地图);
- 对页面中即将用到的跨域域名,提前进行 DNS 解析,语法:
-
使用稳定的公共 DNS 服务器:引导用户配置高性能公共 DNS (如 114.114.114.114、223.5.5.5、8.8.8.8),比 ISP 默认 DNS 解析速度更快、更稳定;
-
域名收敛:减少页面资源的域名数量 (如将静态资源统一放在 1-2 个 CDN 域名下),避免过多域名导致的 DNS 解析开销;
步骤 3:TCP 连接优化(减少握手耗时 + 提升连接效率)
-
启用 HTTP 长连接 (Keep-Alive):
- HTTP/1.1 默认开启 Connection: keep-alive,可复用 TCP 连接处理同域名下的多个资源请求 (如 HTML、CSS、JS、图片),避免每次请求都进行三次握手;
- 服务器端配置合理的长连接超时时间 (如 Nginx 设置 keepalive_timeout 65s),平衡连接复用和服务器资源占用;
-
升级到 HTTP/2 或 HTTP/3:
- HTTP/2:支持多路复用,一个 TCP 连接可同时传输多个请求和响应,解决 HTTP/1.1 的队头阻塞问题;还支持服务端推送 (Server Push),主动推送页面需要的资源;
- HTTP/3:基于 QUIC 协议,使用 UDP 传输,无需三次握手,连接建立速度更快;且天然支持多路复用,丢包影响更小;
-
使用 TCP Fast Open (TFO):启用 TCP 快速打开功能,首次连接后会缓存一个 “cookie”,后续连接可跳过三次握手的前两步,直接建立连接,减少延迟 (需服务器和客户端同时支持);
步骤 4:HTTP 请求发送优化(减少请求体积 + 提升请求效率)
-
请求合并与资源内联:
- 合并小文件:将多个小 CSS/JS 文件合并为一个,减少 HTTP 请求数 (如使用 Webpack 的 splitChunks 或 Gulp 的合并插件);
- 内联关键资源:将首屏渲染必需的 CSS 内联到 HTML 中,避免额外的 CSS 请求阻塞渲染;首屏 JS 也可适当内联;
-
请求参数优化:
- GET 请求参数精简:避免 URL 过长 (浏览器对 URL 长度有限制),非必要参数不传递;
- POST 请求体压缩:对 JSON 等请求体数据,使用 gzip 或 br 压缩后再发送 (需设置请求头 Content-Encoding: gzip);
-
避免空请求和重复请求:
- 拦截无效的接口请求 (如参数为空的查询);通过防抖 (debounce)、节流 (throttle) 控制高频请求 (如搜索框输入、滚动加载);
- 利用请求缓存:对相同参数的 GET 请求,缓存响应结果,短时间内重复请求直接使用缓存;
步骤 5:服务器响应优化(提升响应速度 + 减少响应体积)
-
服务端性能优化:
- 静态资源:使用 CDN 分发,将资源缓存到离用户最近的节点,减少网络传输距离;配置 CDN 的缓存规则,提升缓存命中率;
- 动态接口:优化后端代码逻辑、数据库查询 (如索引优化、SQL 语句优化),减少接口响应时间;使用分布式缓存 (如 Redis) 缓存热点数据;
-
响应内容压缩:
- 服务器端开启 gzip 或 Brotli 压缩,对 HTML、CSS、JS、JSON 等文本类资源进行压缩,压缩率可达 60%-80%;
- Nginx 配置示例 (Brotli 压缩):
http { brotli on; brotli_types text/plain text/css application/json application/javascript text/xml application/xml application/xml+rss text/javascript; }
-
合理设置 HTTP 缓存头:
- 强缓存:通过 Cache-Control + Expires 控制,静态资源 (如图片、字体、打包后的 CSS/JS) 设置较长的缓存时间 (如 Cache-Control: max-age=31536000),并配合文件指纹 (如 app.[hash].js) 实现更新;
- 协商缓存:通过 ETag + Last-Modified 控制,适用于频繁更新的资源,服务器对比资源标识后,决定返回 200 OK 或 304 Not Modified,减少传输体积;
步骤 6:TCP 连接关闭优化(减少资源浪费)
-
避免过早关闭长连接:对于频繁请求的场景 (如 SPA 单页应用),保持长连接复用,避免反复建立 / 关闭连接的开销;
-
优化 TIME_WAIT 状态:服务器端配置 tcp_tw_reuse (Linux 内核参数),允许将 TIME_WAIT 状态的连接复用为新的连接,减少端口占用;
步骤 7:浏览器解析渲染优化(减少回流重绘 + 提升渲染速度)
-
优化 HTML/CSS/JS 加载顺序:
- CSS 优先加载:将 <link rel=“stylesheet”> 放在 <head> 中,让 CSSOM 优先构建,避免渲染树反复重建;避免使用 @import 引入 CSS (会阻塞后续资源加载);
- JS 异步加载:
- 非首屏 JS 添加 defer 属性:脚本异步下载,HTML 解析完成后按顺序执行;
- 独立功能 JS 添加 async 属性:脚本异步下载,下载完成后立即执行 (不保证顺序);
-
减少回流 (Layout) 和重绘 (Paint):
- 使用 DocumentFragment:批量添加 DOM 节点时,先挂载到 DocumentFragment,再一次性插入页面;
- 避免频繁读取布局属性:避免在循环中读取 offsetWidth、scrollTop 等属性 (会触发强制回流),可先缓存到变量中;
- 使用 CSS3 硬件加速:对动画元素添加 transform: translateZ(0) 或 will-change: transform,使元素进入 GPU 层,避免回流;
- 避免复杂选择器:减少嵌套选择器 (如 .parent .child .item),使用类选择器 (如 .item),提升 CSS 选择器匹配效率;
-
优化渲染层合成:
- 减少图层数量:过多独立图层会占用 GPU 内存,导致卡顿;避免对非动画元素使用硬件加速;
- 避免图层爆炸:如列表项过多时,分批渲染 (虚拟列表),防止生成大量独立图层;
-
使用 Web Workers:将复杂计算 (如数据处理、图表渲染计算) 放到 Web Worker 中执行,避免阻塞主线程的解析和渲染;
剑指 Offer 36.二叉搜索树与双向链表
上一篇