从URL输入到页面渲染的全流程

核心流程(9个关键步骤)

记忆口诀:URL解析 → DNS查询 → TCP连接 → HTTP请求 → 服务器响应 → 解析HTML → 构建DOM/CSSOM → 渲染树 → 绘制显示


1. URL解析

  • 浏览器解析输入的URL,提取协议(http/https)、域名、端口、路径等信息
  • 检查URL合法性,判断是搜索关键词还是网址
  • 如果是HTTPS,后续需要进行SSL/TLS握手

2. DNS域名解析(查找IP地址)

流程:浏览器缓存 → 系统缓存 → 路由器缓存 → ISP DNS → 根域名服务器 → 顶级域名服务器 → 权威域名服务器

  • 浏览器先查看自身DNS缓存
  • 查看操作系统hosts文件和系统DNS缓存
  • 向本地DNS服务器(ISP提供)发起查询
  • 递归查询:根DNS → 顶级域名DNS(.com) → 权威DNS
  • 返回目标服务器IP地址

3. 建立TCP连接(三次握手)

流程:SYN → SYN+ACK → ACK

  1. 客户端发送SYN包(请求建立连接)
  2. 服务器返回SYN+ACK包(确认并请求连接)
  3. 客户端发送ACK包(确认连接建立)

为什么三次握手?

  • 确保双方都有发送和接收能力
  • 防止已失效的连接请求突然传到服务器

4. 发送HTTP请求

请求组成:请求行 + 请求头 + 请求体

1
2
3
4
5
GET /index.html HTTP/1.1
Host: www.example.com
User-Agent: Mozilla/5.0
Accept: text/html
Cookie: session_id=xxx
  • 请求方法:GET、POST、PUT、DELETE等
  • 携带Cookie、Token等认证信息
  • 如果是HTTPS,数据会被加密传输

5. 服务器处理并返回响应

响应组成:状态行 + 响应头 + 响应体

1
2
3
4
5
6
7
HTTP/1.1 200 OK
Content-Type: text/html
Content-Length: 1024
Set-Cookie: session_id=xxx

<!DOCTYPE html>
<html>...</html>
  • 状态码:200成功、301重定向、404未找到、500服务器错误
  • 响应头包含内容类型、缓存策略、Cookie等
  • 响应体是HTML、CSS、JS等资源

6. 浏览器解析HTML(构建DOM树)

流程:字节 → 字符 → 令牌 → 节点 → DOM树

  • 解析HTML标签,构建DOM树(Document Object Model)
  • 遇到<link>标签,异步下载CSS(不阻塞DOM构建)
  • 遇到<script>标签:
    • 普通script:阻塞DOM解析,立即下载并执行
    • async:异步下载,下载完立即执行(可能阻塞)
    • defer:异步下载,DOM解析完后按顺序执行

7. 解析CSS(构建CSSOM树)

流程:字节 → 字符 → 令牌 → 节点 → CSSOM树

  • 解析CSS规则,构建CSSOM树(CSS Object Model)
  • CSS解析会阻塞渲染,但不阻塞DOM解析
  • 计算样式优先级(内联 > ID > 类 > 标签)

8. 构建渲染树(Render Tree)

流程:DOM树 + CSSOM树 → 渲染树

  • 将DOM树和CSSOM树合并
  • 只包含可见节点(display:none的节点不在渲染树中)
  • 计算每个节点的样式(Recalculate Style)

9. 布局与绘制

流程:布局(Layout/Reflow) → 绘制(Paint) → 合成(Composite)

布局(Layout/Reflow)

  • 计算每个节点的几何信息(位置、大小)
  • 从根节点递归计算

绘制(Paint)

  • 将渲染树转换为屏幕上的像素
  • 分层绘制(图层、文本、边框等)

合成(Composite)

  • 将多个图层合成最终页面
  • GPU加速(transform、opacity等属性)

性能优化关键点

减少重排(Reflow)

  • 避免频繁修改DOM
  • 使用transform代替top/left
  • 批量修改样式
  • 使用DocumentFragment

减少重绘(Repaint)

  • 避免频繁修改颜色、背景等
  • 使用CSS3动画(GPU加速)

资源加载优化

  • CSS放在<head>中(尽早加载)
  • JS放在</body>前或使用defer/async
  • 使用CDN加速
  • 开启Gzip压缩
  • 使用HTTP/2多路复用
  • 资源懒加载

缓存策略

  • 强缓存:Cache-ControlExpires
  • 协商缓存:ETagLast-Modified

面试口述版本(精简版)

面试官:请描述从输入URL到页面显示的完整过程

回答框架:

“这个过程可以分为9个关键步骤:

  1. URL解析:浏览器解析URL,提取协议、域名、路径等信息

  2. DNS解析:通过DNS查询将域名转换为IP地址,查询顺序是浏览器缓存、系统缓存、路由器缓存、ISP DNS服务器

  3. 建立TCP连接:通过三次握手建立TCP连接,确保客户端和服务器都能正常收发数据

  4. 发送HTTP请求:浏览器构造HTTP请求报文,包含请求方法、请求头、Cookie等信息

  5. 服务器处理:服务器接收请求,处理业务逻辑,返回HTTP响应,包含状态码、响应头和HTML内容

  6. 解析HTML构建DOM树:浏览器解析HTML标签,构建DOM树。遇到CSS异步加载,遇到JS会阻塞解析

  7. 解析CSS构建CSSOM树:解析CSS规则,构建CSSOM树,计算样式优先级

  8. 构建渲染树:将DOM树和CSSOM树合并,生成渲染树,只包含可见节点

  9. 布局与绘制:计算节点的几何信息(布局),将渲染树绘制成像素(绘制),最后合成显示在屏幕上

性能优化方面,可以通过减少重排重绘、优化资源加载顺序、使用缓存策略、开启Gzip压缩等方式提升性能。”


高频追问

Q1: 为什么CSS要放在head中,JS要放在body底部?

  • CSS放head:尽早加载样式,避免页面闪烁(FOUC)
  • JS放底部:避免阻塞DOM解析,提升首屏渲染速度
  • 现代方案:JS使用deferasync属性

Q2: 重排和重绘的区别?

  • 重排(Reflow):元素几何属性变化(位置、大小),需要重新计算布局,开销大
  • 重绘(Repaint):元素样式变化(颜色、背景),不影响布局,开销小
  • 重排一定重绘,重绘不一定重排

Q3: 如何减少重排?

  • 批量修改DOM(使用DocumentFragment
  • 使用transform代替top/left
  • 避免逐条修改样式,使用class
  • 将DOM离线后修改(display:none
  • 使用requestAnimationFrame

Q4: 浏览器的缓存策略?

  • 强缓存Cache-Control(优先)、Expires,直接使用缓存,不请求服务器
  • 协商缓存ETag/If-None-MatchLast-Modified/If-Modified-Since,询问服务器是否可用缓存

Q5: TCP为什么是三次握手,四次挥手?

  • 三次握手:确保双方收发能力正常,防止失效请求
  • 四次挥手:因为TCP是全双工,需要双方分别关闭发送通道,所以需要四次