当你在浏览器地址栏中输入 https://coooder-blog.vercel.app/ 并按下回车键时,看似简单的操作触发了一个复杂的流程。从这一刻开始,浏览器将经历四个主要阶段:网络请求、服务器处理、资源解析、页面渲染,最终将远程服务器上的代码转换成你眼前生动的博客网页。
一、网络请求阶段:建立连接获取资源
这是整个流程的第一阶段,浏览器需要通过网络获取网页的基础资源。
1. URL 解析与导航决策
浏览器首先需要理解你输入的 URL,一个完整的 URL 通常包含以下部分:
https://www.example.com:443/path/to/page?query=value#fragment
| | | | | |
协议 主机名(域名) 端口 路径 查询参数 片段标识符
浏览器会进行以下解析:
- 协议识别:
https://告诉浏览器使用安全的 HTTP 协议 - 域名提取:
www.example.com是需要解析的目标地址 - 端口确定:HTTPS 默认使用 443 端口,HTTP 使用 80 端口
- 路径解析:确定请求的具体资源路径
浏览器还会进行一系列安全和策略检查:
- Same-Origin Policy:检查是否违反同源策略
- Content Security Policy (CSP):验证内容安全策略
- HSTS 检查:如果之前访问过该域名,检查是否强制 HTTPS
📌 现代浏览器会在你输入 URL 时就开始预解析 DNS,这叫做 DNS 预取(DNS Prefetch),为后续步骤节省时间。更加激进的策略还有 Preconnect,不仅解析 DNS,还提前建立 TCP 链接和 TLS 握手,但会消耗更多资源。
2. DNS 解析:从域名到 IP 地址
DNS 解析是一个递归查询过程,遵循严格的层次结构:
用户输入:
www.example.com
查询层次:
1. 浏览器 DNS 缓存
2. 操作系统 DNS 缓存
3. 路由器 DNS 缓存
4. ISP DNS 服务器
5. 根域名服务器 (.)
6. 顶级域名服务器 (.com)
7. 权威域名服务器 (
example.com
)
缓存机制的威力:
- 浏览器缓存:通常缓存几分钟到几小时
- 系统缓存:由操作系统管理,生存时间更长
- ISP 缓存:互联网服务提供商的缓存服务器
现代 DNS 相关优化:
- DNS over HTTPS (DoH):通过 HTTPS 加密 DNS 查询,提高隐私和安全性
- DNS over TLS (DoT):另一种加密 DNS 查询的方式
- DNS 预取:
<link rel="dns-prefetch" href="//example.com">提前解析域名
⏱️ DNS 解析通常需要 20–120 毫秒,但通过缓存机制,大部分查询可以在几毫秒内完成。
3. 建立网络连接
TCP 三次握手(及四次挥手)
在获得 IP 地址后,浏览器需要与服务器建立 TCP 连接:
客户端 → 服务器:SYN (同步序列号)
客户端 ← 服务器:SYN-ACK (同步-确认)
客户端 → 服务器:ACK (确认)
连接建立成功 ✅
TLS 握手过程
对于 HTTPS 连接,还需要额外的 TLS 握手:
- Client Hello:客户端发送支持的加密套件列表
- Server Hello:服务器选择加密套件并发送证书
- 证书验证:客户端验证服务器证书的有效性
- 密钥交换:使用非对称加密交换对称密钥
- 握手完成:双方确认加密通道建立
连接复用技术:
- HTTP/1.1 Keep-Alive:复用 TCP 连接处理多个请求
- HTTP/2 多路复用:单个连接同时处理多个流
- HTTP/3 QUIC:基于 UDP 的快速连接建立
🔒 虽然 TLS 握手会增加延迟,但现代优化(如 TLS 1.3)已将握手时间减少到 1–2 个 RTT。
4. 发送 HTTP 请求
浏览器构建一个详细的 HTTP 请求:
GET /index.html HTTP/1.1
Host:
www.example.com
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: zh-CN,zh;q=0.9,en;q=0.8
Accept-Encoding: gzip, deflate, br
Connection: keep-alive
Cache-Control: max-age=0
关键请求头解析:
Host:指定目标服务器User-Agent:浏览器标识信息Accept:客户端可接受的内容类型Accept-Encoding:支持的压缩算法Cache-Control:缓存策略指令
常用请求方法:
GET:获取资源(幂等、安全)POST:提交数据(非幂等)PUT:更新资源(幂等)DELETE:删除资源(幂等)
二、服务器处理阶段:生成响应内容
服务器接收请求后,需要处理业务逻辑并生成响应。
1. 服务器处理流程
虽然这主要是后端的工作,但理解服务器处理过程有助于前端优化:
- 请求路由:根据 URL 路径确定处理器
- 身份验证:验证用户身份和权限
- 业务逻辑处理:执行应用程序代码
- 数据库查询:获取动态数据
- 模板渲染:生成最终 HTML
- 压缩优化:使用 Gzip / Brotli 压缩响应
2. 常见的响应状态码
2xx 成功状态
200 OK - 请求成功
201 Created - 资源已创建
204 No Content - 成功但无内容
3xx 重定向状态
301 Moved Permanently - 永久重定向
302 Found - 临时重定向
304 Not Modified - 资源未修改
4xx 客户端错误
400 Bad Request - 请求语法错误
401 Unauthorized - 需要身份验证
402 Payment Required- 需要付款
403 Forbidden - 禁止访问
404 Not Found - 资源不存在
5xx 服务器错误
500 Internal Server Error - 服务器内部错误
502 Bad Gateway - 网关错误
503 Service Unavailable - 服务不可用
📌 402 一直没被广泛实际使用过,最近 Coinbase 提出的 x402 协议尝试把 402 与 Crypto 支付结合起来。
3. 接收和处理响应
服务器生成响应后,浏览器开始接收并处理响应数据。
响应头的重要信息:
HTTP/1.1 200 OK
Content-Type: text/html; charset=UTF-8
Content-Length: 12345
Content-Encoding: gzip
Cache-Control: public, max-age=3600
Last-Modified: Wed, 15 Dec 2024 10:00:00 GMT
ETag: "abc123def456"
Set-Cookie: sessionid=abc123; HttpOnly; Secure
缓存策略处理:
- 强缓存:
Cache-Control: max-age=3600表示缓存 1 小时 - 协商缓存:通过
ETag或Last-Modified验证资源是否更新 - 缓存位置:内存缓存 → 磁盘缓存 → 推送缓存
流式处理:
现代浏览器支持边接收边解析 HTML:
- 边接收边解析:不等待完整响应即开始解析
- 渐进式渲染:页面内容逐步显示
- 优先级调度:关键资源优先处理
三、资源解析阶段:构建页面结构
浏览器接收到 HTML 响应后,开始解析并构建页面的基础结构。
1. HTML 解析与 DOM 构建
词法分析阶段:
HTML 解析器将文本转换为 token:
<div class="container">Hello World</div>
// 转换为 tokens: // StartTag: div, attributes: [class="container"] //
Characters: "Hello World" // EndTag: div
构建 DOM 树:
解析器使用栈结构维护元素的嵌套关系:
Document
└── html
├── head
│ ├── title
│ └── meta
└── body
├── div.container
│ └── "Hello World"
└── script
解析中断与恢复:
当遇到阻塞资源时:
- 同步脚本:暂停 DOM 构建,下载并执行脚本
- 样式表:继续 DOM 构建,但可能阻塞渲染
async脚本:异步下载,不阻塞 DOM 构建defer脚本:异步下载,DOM 完成后执行
📖 HTML 解析器是容错的,能处理格式不完整的 HTML,这也是许多“不规范”网页仍能正常显示的原因。
2. CSS 解析与样式计算
CSSOM 构建:
CSS 解析器会构建样式对象模型(CSSOM):
body {
font-size: 16px;
}
.container {
width: 100%;
padding: 20px;
}
.container p {
color: #333;
line-height: 1.5;
}
样式计算过程:
- 样式收集:从多个来源收集样式规则
- 浏览器默认样式
- 用户代理样式表
- 用户自定义样式
- 网页 CSS 文件
- 选择器匹配:确定哪些规则适用于每个元素
- 优先级计算:根据 CSS 特殊性规则确定最终样式
/* 特殊性计算:内联样式 > ID > 类 / 属性 / 伪类 > 元素 / 伪元素 */
div {
color: red;
} /* 特殊性: 0,0,0,1 */
.highlight {
color: blue;
} /* 特殊性: 0,0,1,0 */
#main {
color: green;
} /* 特殊性: 0,1,0,0 */
- 值计算:处理继承、初始值和计算值
- 指定值:CSS 规则声明的值
- 计算值:相对值转换为绝对值
- 使用值:最终用于渲染的值
现代 CSS 优化:
- 选择器从右到左匹配
- 样式缓存:避免重复计算
- 增量更新:只重新计算受影响的元素
四、页面渲染阶段:生成最终界面
有了 DOM 树和样式信息后,浏览器开始布局和绘制,生成用户最终看到的界面。
1. 布局计算(Layout)
渲染树构建:
将 DOM 树和 CSSOM 合并生成渲染树:
- 只包含需要显示的元素(
display: none的元素被排除) - 每个节点包含样式信息
- 保持文档顺序
布局算法:
- 全局布局:从根元素开始计算
- 块级格式化上下文(BFC):处理块级元素布局
- 内联格式化上下文(IFC):处理内联元素布局
- 弹性布局(Flexbox):现代一维布局方案
- 网格布局(Grid):二维布局系统
几何属性计算:
对每个元素,浏览器会计算类似如下的信息:
// 位置信息
x: 100, // 相对于父容器的 x 坐标
y: 50, // 相对于父容器的 y 坐标
// 尺寸信息
width: 300, // 内容宽度
height: 200, // 内容高度
// 盒模型
margin: { ... },
border: { ... },
padding: { ... },x w
布局优化策略:
- 部分重布局:只重新计算受影响的子树
- 布局边界:某些 CSS 属性可以限制布局传播
- 虚拟化:长列表只渲染可视区域内的元素
⚡ 性能提示:频繁的布局计算是性能瓶颈,避免在循环中多次读取会触发布局的属性。
2. 绘制准备(Paint)
绘制层级划分:
浏览器将页面分解为多个绘制层:
- 普通文档流:大部分元素在同一层
- 层叠上下文:满足以下条件的元素会创建新层级,例如:
z-index不为auto的定位元素opacity小于 1 的元素- 使用
transform的元素 position: fixed的元素
绘制指令生成:
对每个图层生成绘制指令列表,例如:
[
{ type: "fillRect", color: "#ffffff", x: 0, y: 0, width: 300, height: 200 },
{
type: "drawText",
text: "Hello",
font: "16px Arial",
color: "#000",
x: 10,
y: 30,
},
{
type: "strokeRect",
color: "#cccccc",
x: 0,
y: 0,
width: 300,
height: 200,
lineWidth: 1,
},
];
绘制优化:
- 脏区域标记:只重绘发生变化的区域
- 绘制指令缓存:避免重复生成
- 图层合并:在合适时机合并图层以减少内存使用
3. 合成与光栅化
图块(Tile)分割:
大的绘制层被分割成 256×256 或 512×512 的图块:
页面层级 (1920x1080)
├── Tile[0,0] (256x256)
├── Tile[1,0] (256x256)
├── Tile[0,1] (256x256)
└── ...(其他图块)
光栅化过程:
- 优先级排序:视口内的图块优先处理
- 工作线程池:多个光栅化线程并行工作
- GPU 加速:利用显卡加速绘制
- 位图生成:将矢量指令转换为像素数据
合成器(Compositor)职责:
- 图层管理:跟踪所有图层状态
- 视口裁剪:只处理可见区域图块
- 变换应用:应用 CSS
transform等效果 - 最终合成:将所有图层合成为最终图像
🖥️ 现代浏览器会尽可能使用 GPU 进行合成,对动画和滚动尤为重要。
4. 显示与交互准备
帧缓冲与显示:
最终图像数据被写入帧缓冲区:
帧缓冲区 → 显卡 → 显示器
刷新率示例:
- 60 fps ≈ 16.67 ms/帧
- 120 fps ≈ 8.33 ms/帧
- 144 fps ≈ 6.94 ms/帧
JavaScript 执行与事件循环:
一旦 DOM 构建完成,就会开始执行 JavaScript:
- 脚本加载顺序:
<script src="sync.js"></script>
<!-- 同步执行 -->
<script src="async.js" async></script>
<!-- 异步执行 -->
<script src="defer.js" defer></script>
<!-- 延迟执行 -->
- 事件循环:
- 调用栈:执行 JavaScript 代码
- 任务队列:处理异步任务
- 微任务队列:处理 Promise 等高优先级任务
- 事件监听器绑定示例:
// DOMContentLoaded:DOM 构建完成
JavaScript 执行
一旦 DOM 构建完成,浏览器就会开始执行 JavaScript,并启动事件循环。
- 脚本加载顺序:
<script src="sync.js"></script>
<!-- 同步执行:阻塞解析,按顺序执行 -->
<script src="async.js" async></script>
<!-- 异步执行:下载与解析并行,下载完立刻执行,不保证顺序 -->
<script src="defer.js" defer></script>
<!-- 延迟执行:下载与解析并行,DOM 完成后按顺序执行 -->
- 事件循环启动:
- 调用栈(Call Stack):执行 JavaScript 代码
- 任务队列(Task Queue / Macro Task Queue):处理
setTimeout、setInterval、事件回调等异步任务 - 微任务队列(Microtask Queue):处理
Promise.then、MutationObserver等高优先级任务
浏览器在每一轮事件循环中大致会:
- 取出一个宏任务执行完毕
- 清空当前产生的所有微任务
- 进行必要的渲染更新
- 事件监听器绑定示例:
// DOMContentLoaded:DOM 构建完成
// 适合只依赖 DOM、不依赖图片等资源的初始化逻辑
document.addEventListener("DOMContentLoaded", function () {
console.log("DOM 已准备就绪");
});
// load:所有资源(图片、样式表等)加载完成
// 适合依赖完整资源的逻辑
window.addEventListener("load", function () {
console.log("页面完全加载完成");
});
用户交互就绪
当关键脚本执行完毕、事件监听器绑定完成后,页面进入可交互状态:
- 可点击元素:按钮、链接等可以响应用户操作
- 表单可用:输入框可以正常接受用户输入
- 滚动流畅:页面支持平滑滚动和惯性滚动
- 动画开始:CSS 动画和 JavaScript 驱动的动画开始执行
现代浏览器的性能优化
预加载技术
<!-- DNS 预解析 -->
<link
rel="dns-prefetch"
href="//
example.com
"
/>
<!-- 预连接 -->
<link
rel="preconnect"
href="
https://fonts.googleapis.com
"
/>
<!-- 资源预加载 -->
<link rel="preload" href="critical.css" as="style" />
<link rel="preload" href="hero.jpg" as="image" />
<!-- 页面预取(适合预测用户下一步可能访问的页面) -->
<link rel="prefetch" href="/next-page.html" />
关键渲染路径优化
- 最小化关键资源:
- 减少阻塞渲染的 CSS 文件
- 内联关键 CSS
- 异步加载非关键 JavaScript
- 减少关键字节数:
- 压缩 HTML、CSS、JavaScript
- 移除未使用的代码
- 使用现代图片格式 (WebP、AVIF)
- 缩短关键路径长度:
- 减少重定向
- 避免 render-blocking 资源
- 优化资源加载顺序
现代 Web 标准
- HTTP/3:基于 QUIC 协议,减少连接建立延迟
- Server Push(HTTP/2):服务器可主动推送关键资源(虽然现在使用减少,但原理仍然重要)
- Service Worker:离线缓存和后台任务处理,支持 PWA 能力
- WebAssembly:适合高性能计算场景
- Progressive Web App (PWA):提供接近原生应用的体验(离线、安装、通知等)
性能测量与监控
Core Web Vitals
Google 提出的三个核心指标:
- LCP (Largest Contentful Paint):最大内容绘制时间
- 理想值:< 2.5 秒
- 关注首屏主要内容(如 Hero 图、主标题)何时完成渲染
- FID (First Input Delay):首次输入延迟
- 理想值:< 100 毫秒
- 测量用户首次交互(点击、输入)到浏览器开始处理该事件之间的延迟
- CLS (Cumulative Layout Shift):累积布局位移
- 理想值:< 0.1
- 衡量页面视觉稳定性,防止“内容突然跳动”
其他重要指标
- FCP (First Contentful Paint):首次内容绘制
- TTI (Time to Interactive):页面真正可交互的时间
- TTFB (Time to First Byte):首字节时间,反映后端和网络延迟
- Speed Index:页面可见内容加载速度的综合评分
性能测试工具示例
// 使用 Performance API 监听关键性能条目
const observer = new PerformanceObserver((list) => {
for (const entry of list.getEntries()) {
console.log(entry.name, entry.startTime, entry.duration);
}
});
observer.observe({ entryTypes: ["navigation", "resource", "paint"] });
// 测量自定义功能耗时
performance.mark("feature-start");
// ... 执行某些操作
performance.mark("feature-end");
performance.measure("feature-duration", "feature-start", "feature-end");
总结
以上整个过程看似复杂,但现代浏览器通过预加载、缓存、多进程 / 多线程、GPU 合成等优化手段,把用户的等待压缩到秒级甚至毫秒级。
从 URL 输入到页面完全可交互,这条“关键路径”是前端性能优化的主战场。理解这些细节,有助于我们在遇到性能瓶颈时更快地定位问题,并做出有针对性的优化。
Table of Contents
- 一、网络请求阶段:建立连接获取资源
- 1. URL 解析与导航决策
- 2. DNS 解析:从域名到 IP 地址
- 3. 建立网络连接
- TCP 三次握手(及四次挥手)
- TLS 握手过程
- 4. 发送 HTTP 请求
- 二、服务器处理阶段:生成响应内容
- 1. 服务器处理流程
- 2. 常见的响应状态码
- 3. 接收和处理响应
- 三、资源解析阶段:构建页面结构
- 1. HTML 解析与 DOM 构建
- 2. CSS 解析与样式计算
- 四、页面渲染阶段:生成最终界面
- 1. 布局计算(Layout)
- 2. 绘制准备(Paint)
- 3. 合成与光栅化
- 4. 显示与交互准备
- JavaScript 执行
- 用户交互就绪
- 现代浏览器的性能优化
- 预加载技术
- 关键渲染路径优化
- 现代 Web 标准
- 性能测量与监控
- Core Web Vitals
- 其他重要指标
- 性能测试工具示例
- 总结