从URL输入到页面渲染全过程
核心流程概览
URL输入 → DNS解析 → TCP连接 → HTTP请求 → 服务器响应 → 浏览器解析 → 渲染页面
整个过程可以分为网络请求阶段和浏览器渲染阶段两大核心部分。
阶段一:网络请求阶段
1. URL解析与输入处理
地址栏输入处理
- 协议识别:浏览器判断输入是URL还是搜索关键词
- URL规范化:补全协议(如
www.example.com→https://www.example.com) - 编码处理:对特殊字符进行URL编码(如空格 →
%20)
面试要点
- URL结构:
协议://域名:端口/路径?查询参数#锚点 - 同源策略:协议、域名、端口三者相同才同源
- URL编码:
encodeURI()和encodeURIComponent()的区别
2. DNS域名解析
解析流程(递归查询 + 迭代查询)
浏览器缓存 → 操作系统缓存 → hosts文件 → 本地DNS服务器 → 根域名服务器 → 顶级域名服务器 → 权威域名服务器
详细步骤
-
浏览器DNS缓存
- 浏览器维护自己的DNS缓存表
- 缓存时间由TTL(Time To Live)决定
- 可通过
chrome://net-internals/#dns查看
-
操作系统缓存
- Windows:
ipconfig /displaydns - Linux/Mac:
/etc/hosts文件
- Windows:
-
本地DNS服务器查询
- 通常由ISP(互联网服务提供商)提供
- 如果缓存中没有,开始递归查询
-
DNS递归/迭代查询
- 递归查询:客户端 → 本地DNS服务器(客户端只发一次请求)
- 迭代查询:本地DNS服务器 → 根/顶级/权威服务器(多次往返)
- 最终获取IP地址并返回
面试要点
- DNS预解析:
<link rel="dns-prefetch" href="//example.com"> - DNS查询类型:A记录(IPv4)、AAAA记录(IPv6)、CNAME(别名)
- DNS优化:减少DNS查询次数、使用CDN、DNS预解析
3. TCP连接建立(三次握手)
三次握手过程
客户端 服务器
| |
|-------- SYN (seq=x) -------->|
| |
|<-- SYN-ACK (seq=y, ack=x+1) -|
| |
|------ ACK (ack=y+1) -------->|
| |
| 连接建立,开始传输数据 |
详细说明
-
第一次握手(SYN)
- 客户端发送SYN包,序列号seq=x,进入SYN_SENT状态
- 目的:请求建立连接,告知服务器自己的初始序列号
-
第二次握手(SYN-ACK)
- 服务器发送SYN-ACK包,序列号seq=y,确认号ack=x+1,进入SYN_RCVD状态
- 目的:确认收到客户端请求,同时发送自己的连接请求
-
第三次握手(ACK)
- 客户端发送ACK包,确认号ack=y+1,进入ESTABLISHED状态
- 服务器收到后也进入ESTABLISHED状态
- 目的:确认收到服务器的响应,连接正式建立
为什么需要三次握手?
- 防止历史连接:如果客户端发送的SYN包是旧的(网络延迟),服务器可以通过第三次握手判断并拒绝
- 同步序列号:双方确认对方的初始序列号,为可靠传输做准备
- 确认双方通信能力:确保客户端和服务器都能正常收发数据
面试要点
- 半连接队列:服务器在SYN_RCVD状态维护的队列,可能遭受SYN Flood攻击
- SYN Cookie:防御SYN Flood攻击的机制
- TCP vs UDP:TCP可靠但慢,UDP快速但不可靠
4. TLS/SSL握手(HTTPS)
TLS 1.2握手流程(4次往返)
-
Client Hello
- 客户端发送:支持的TLS版本、加密套件列表、随机数(Client Random)
-
Server Hello
- 服务器选择:TLS版本、加密套件、随机数(Server Random)
- 发送服务器证书(包含公钥)
-
客户端验证与密钥交换
- 验证证书有效性(CA签名、域名匹配、有效期)
- 生成预主密钥(Pre-Master Secret),用服务器公钥加密发送
- 双方根据Client Random + Server Random + Pre-Master Secret 计算主密钥
-
完成握手
- 双方发送Finished消息,使用主密钥加密
- 验证成功后,开始加密通信
TLS 1.3优化(1次往返)
- 客户端在第一次就发送密钥交换信息
- 服务器可以直接响应加密数据,减少往返次数
面试要点
- 证书链验证:根CA → 中间CA → 服务器证书
- 对称加密 vs 非对称加密:握手用非对称,传输用对称(性能考虑)
- 前向安全性:即使私钥泄露,历史会话仍安全
5. HTTP请求发送
请求报文结构
请求行:方法 路径 协议版本
请求头:Host, User-Agent, Accept, Cookie等
空行
请求体:POST/PUT数据
关键请求头
- Host:目标主机(HTTP/1.1必需,支持虚拟主机)
- User-Agent:浏览器标识
- Accept:可接受的响应类型
- Cookie:携带的Cookie信息
- Referer:来源页面
- Cache-Control:缓存控制
HTTP/1.1 vs HTTP/2
HTTP/1.1问题:
- 队头阻塞:同一连接上请求必须串行
- 连接数限制:浏览器对同一域名限制6-8个连接
- 头部冗余:每次请求都发送完整头部
HTTP/2优势:
- 多路复用:单一连接上并行传输多个请求/响应
- 头部压缩:HPACK算法压缩头部
- 服务器推送:服务器主动推送资源
面试要点
- HTTP方法:GET(幂等)、POST(非幂等)、PUT、DELETE、PATCH
- 幂等性:多次执行结果相同
- 安全性:GET、HEAD是安全的,不会修改服务器状态
6. 服务器处理与响应
服务器处理流程
- 接收请求:Web服务器(Nginx、Apache)接收
- 路由解析:根据URL路径路由到对应处理程序
- 业务处理:执行后端逻辑(数据库查询、业务计算等)
- 生成响应:构建HTTP响应报文
响应报文结构
状态行:协议版本 状态码 状态描述
响应头:Content-Type, Content-Length, Set-Cookie等
空行
响应体:HTML、JSON等数据
关键响应头
- Content-Type:响应体类型(
text/html,application/json) - Content-Length:响应体长度
- Set-Cookie:设置Cookie
- Cache-Control:缓存策略
- Location:重定向地址(3xx状态码)
7. TCP连接关闭(四次挥手)
四次挥手过程
客户端 服务器
| |
|-------- FIN (seq=x) -------->|
| |
|<-- ACK (ack=x+1) ------------|
| |
|<-- FIN (seq=y) --------------|
| |
|------ ACK (ack=y+1) -------->|
| |
| 连接关闭 |
为什么需要四次挥手?
- 半关闭状态:一方发送FIN后,仍可接收数据
- 数据完整性:确保双方数据都发送完毕
- TIME_WAIT状态:客户端最后ACK后等待2MSL(最大报文段生存时间),防止旧连接数据包干扰新连接
面试要点
- TIME_WAIT的作用:确保最后一个ACK到达,处理可能的重传
- CLOSE_WAIT过多:通常是应用层未正确关闭连接
- SO_REUSEADDR:允许端口复用,解决TIME_WAIT问题
阶段二:浏览器渲染阶段
8. 浏览器解析响应
响应处理流程
-
Content-Type判断
text/html→ HTML解析器处理application/json→ JSON解析image/png→ 图片解码application/javascript→ JavaScript引擎执行
-
字符编码解析
- 根据HTTP响应头
Content-Type: text/html; charset=utf-8 - 或HTML中的
<meta charset="utf-8"> - 将字节流转换为字符流
- 根据HTTP响应头
9. HTML解析与DOM构建
解析流程
字节流 → 字符流 → Token(令牌)→ 节点(Node)→ DOM树
详细步骤
-
词法分析(Tokenization)
- HTML解析器将字符流转换为Token
- Token类型:开始标签、结束标签、文本、注释等
-
语法分析(DOM树构建)
- 根据Token构建DOM节点
- 遇到开始标签创建元素节点
- 遇到文本创建文本节点
- 遇到结束标签闭合当前节点
-
DOM树构建算法
- 使用栈结构维护节点层级关系
- 遇到开始标签入栈,遇到结束标签出栈
解析过程中的资源加载
- 遇到
<script>:阻塞解析,下载并执行脚本 - 遇到
<link>:异步下载CSS,不阻塞解析 - 遇到
<img>:异步下载图片,不阻塞解析
面试要点
- 解析阻塞:
<script>会阻塞DOM构建,async和defer的区别 - DOMContentLoaded vs load:DOMContentLoaded是DOM构建完成,load是所有资源加载完成
- 回流与重绘:DOM操作可能触发回流(reflow)和重绘(repaint)
10. CSS解析与CSSOM构建
CSS解析流程
CSS字节流 → 字符流 → Token → CSS规则 → CSSOM树
CSSOM树结构
- 规则匹配:根据选择器匹配元素
- 样式计算:计算每个元素的最终样式(继承、层叠、优先级)
- CSSOM树:表示样式规则之间的层级关系
样式计算关键点
- 继承:某些属性会从父元素继承(如
color,font-size) - 层叠:多个规则作用于同一元素时的优先级
- 优先级:
!important> 内联样式 > ID选择器 > 类选择器 > 标签选择器
面试要点
- CSS阻塞渲染:CSS会阻塞渲染树的构建,但不阻塞DOM构建
- 选择器性能:从右到左匹配,避免深层嵌套和通配符
- 重排(Reflow):改变元素几何属性(宽高、位置)触发
- 重绘(Repaint):改变元素外观属性(颜色、背景)触发
11. 渲染树(Render Tree)构建
构建过程
- 遍历DOM树:从根节点开始
- 可见性检查:过滤不可见节点(
display: none不加入渲染树,visibility: hidden会加入) - 样式匹配:为每个可见节点匹配CSS规则
- 计算样式:计算最终的计算样式(computed style)
渲染树特点
- 只包含可见元素
- 每个节点包含完整的样式信息
- 是DOM和CSSOM的结合
12. 布局(Layout/Reflow)
布局流程
- 计算几何信息:根据CSS盒模型计算每个元素的位置和大小
- 盒模型计算:
width/height:内容区域padding:内边距border:边框margin:外边距
- 定位计算:
- 正常流(Normal Flow)
- 浮动(Float)
- 绝对定位(Absolute)
- 固定定位(Fixed)
布局算法
- 块级格式化上下文(BFC):独立的布局环境
- 弹性布局(Flexbox):一维布局
- 网格布局(Grid):二维布局
面试要点
- 触发回流的操作:修改DOM结构、改变元素尺寸、改变窗口大小
- 优化策略:批量DOM操作、使用
transform代替top/left、使用DocumentFragment - BFC应用:清除浮动、防止margin重叠、自适应布局
13. 分层(Layer)与合成(Composite)
分层原因
- 提升性能:某些元素独立一层,变化时只需重绘该层
- 硬件加速:利用GPU加速渲染
分层触发条件
transform、opacity、will-change等属性- 视频、Canvas、iframe等元素
- 有层叠上下文(z-index)的元素
合成流程
- 绘制列表:为每层生成绘制指令
- 栅格化(Rasterization):将绘制指令转换为位图
- 合成(Compositing):将各层位图合成为最终画面
面试要点
- GPU加速:使用
transform和opacity触发硬件加速 - 重排 vs 重绘 vs 合成:重排最耗性能,合成性能最好
- will-change:提前告知浏览器元素将变化,优化分层
14. 绘制(Paint)与显示
绘制流程
- 主线程绘制:生成绘制指令列表
- 合成线程处理:将绘制指令提交给合成线程
- GPU栅格化:将绘制指令转换为像素
- 显示:将最终画面显示到屏幕
渲染优化
- 避免强制同步布局:避免在读取布局信息前修改DOM
- 使用
requestAnimationFrame:在下一帧渲染前执行动画 - 虚拟滚动:只渲染可见区域
- 代码分割:按需加载资源
性能优化关键点
网络阶段优化
- DNS优化:DNS预解析、使用CDN
- TCP优化:HTTP/2、HTTP/3(QUIC)
- 资源优化:压缩、缓存、CDN、资源优先级
渲染阶段优化
- 关键渲染路径优化:内联关键CSS、延迟非关键CSS
- JavaScript优化:
async/defer、代码分割、Tree Shaking - 避免强制同步布局:批量读取布局信息
- 使用合成层:
transform、opacity触发GPU加速
面试高频问题
- DNS解析过程?如何优化?
- 三次握手和四次挥手的详细过程?为什么需要?
- HTTPS握手过程?TLS 1.3的优化?
- HTTP/1.1和HTTP/2的区别?
- 浏览器渲染流程?重排和重绘的区别?
- 如何优化首屏渲染时间?
<script>标签的async和defer区别?- DOMContentLoaded 和 load 事件的区别?