在前端面试中,经常会遇到一个经典问题:
浏览器输入一个 URL 后,页面是如何一步步渲染出来的?
这个问题看似简单,其实能够串联起很多前端基础知识,例如:
- 浏览器渲染机制
- DOM 与 CSSOM
- 回流与重绘
- HTTP 请求结构
- GET 与 POST 区别
- TCP 三次握手
如果脑海中能够形成一张完整的流程图,这个问题就会非常容易理解。
浏览器渲染流程大致如下:
HTML解析
↓
DOM Tree + CSSOM Tree
↓
Render Tree
↓
Layout
↓
Paint
↓
Layer
↓
Composite
也可以总结为:
HTML解析 → DOM树 + CSSOM树 → Render Tree → Layout → Paint → Layer → Composite
下面我们按照浏览器实际执行顺序进行分析。
一、HTML 解析:构建 DOM Tree
当浏览器拿到一个 URL 时,首先会发起 网络请求下载 HTML 文件。
HTML 的解析方式是 流式解析(Streaming Parsing) ,也就是说浏览器会:
边下载 HTML
边解析 HTML
浏览器中的 HTML 解析器会把 HTML 标签逐步解析成 DOM Tree(文档对象模型) 。
例如:
<body>
<div class="title">Hello</div>
</body>
浏览器会在内存中构建类似这样的结构:
Document
└── body
└── div
└── text
DOM Tree 本质上就是 HTML 页面结构在浏览器中的树形表示。
二、CSS 解析:构建 CSSOM Tree
在解析 HTML 的过程中,如果浏览器遇到以下标签:
<link>
<style>
这些 CSS 资源,浏览器会发起 CSS 请求。
CSS 下载完成后,会由 CSS 解析器解析生成 CSSOM Tree。
例如:
.title {
color: red;
}
浏览器会把这些样式规则解析为一棵 CSSOM 树。
此时浏览器内部会同时存在两棵树:
DOM Tree
CSSOM Tree
这两棵树是浏览器进行页面渲染的重要基础。
三、JavaScript 为什么会阻塞 DOM
在解析 HTML 的过程中,如果浏览器遇到:
<script src="index.js"></script>
默认情况下 JavaScript 会阻塞 DOM 的解析。
原因是 JavaScript 可以动态操作 DOM,例如:
document.title = "Hello"
document.body.appendChild(...)
如果浏览器继续解析 HTML,可能会导致 DOM 状态不一致。
因此浏览器会执行以下流程:
暂停 HTML 解析
↓
交给 JS 引擎执行脚本
↓
脚本执行完成
↓
继续解析 HTML
所以在实际开发中,我们通常会:
- 将
script放在 body 底部 - 或者使用
defer/async属性
来避免阻塞页面渲染。
四、生成 Render Tree(渲染树)
当 DOM Tree 和 CSSOM Tree 都构建完成之后,浏览器会把两者合并生成:
Render Tree(渲染树)
需要注意的是:
Render Tree 并不等于 DOM Tree。
渲染树只包含 需要显示的元素。
例如:
display: none
的元素不会出现在 Render Tree 中。
也就是说:
- DOM Tree 描述页面结构
- CSSOM Tree 描述样式规则
- Render Tree 描述最终需要渲染的内容
五、Layout(回流 / Reflow)
生成 Render Tree 后,浏览器会进入 Layout 阶段,也叫 回流(Reflow) 。
在这个阶段,浏览器会根据以下信息计算每个元素的位置和尺寸:
- 盒模型
- 元素宽高
- margin / padding
- position
- 页面结构关系
Layout 的结果是确定每个元素在页面中的 几何位置和大小。
回流是浏览器渲染过程中 开销较大的操作之一。
六、Paint(绘制)
Layout 完成后,浏览器会进入 Paint(绘制)阶段。
在这个阶段,浏览器会把元素的视觉内容绘制出来,例如:
- 文字
- 背景
- 边框
- 阴影
- 颜色
此时页面已经具备完整的视觉效果。
七、Layer 与 Composite(图层合成)
在现代浏览器中,页面通常会被拆分成多个 图层(Layer) 。
例如以下属性可能会创建新的合成层:
transformopacityposition: fixed- CSS 动画
浏览器会把这些图层交给 GPU 进行合成(Composite) 。
整个流程可以理解为:
Paint
↓
Layer
↓
Composite(GPU合成)
↓
最终显示到屏幕
这种方式可以提高动画和滚动时的性能。
八、前端渲染优化
理解浏览器渲染流程后,就可以从 HTML、CSS、JavaScript 三个方面进行优化。
1 HTML 优化
首先是 HTML 结构优化。
使用语义化标签
例如:
header
nav
article
footer
语义化标签不仅有利于 SEO,还可以提升代码的可读性和可维护性。
减少频繁 DOM 操作
频繁操作 DOM 会影响性能,可以先缓存节点或者使用 文档碎片进行批量更新:
document.createDocumentFragment()
图片懒加载
对于非首屏图片可以使用懒加载,减少页面初始渲染压力。
2 CSS 优化
CSS 优化主要包括以下几点。
避免使用通配符选择器
例如:
* {}
通配符会匹配所有元素,性能较低。
减少 HTTP 请求
小图标可以转为 base64,减少请求数量;但较大的资源仍然建议使用外链。
抽离通用样式
例如:
btn
card
container
这样可以减少重复代码,提高复用性。
合理使用 CSS 变量
CSS 变量可以统一主题样式,方便维护。
避免滥用 !important
过多使用 !important 会破坏样式优先级规则,导致样式难以维护。
在现代前端项目中,也常使用 TailwindCSS 原子化 CSS。它通过原子类组合样式,不需要大量编写 CSS,同时支持按需编译,体积更加可控。
3 JavaScript 优化
JavaScript 方面主要注意以下几点。
script 放在页面底部
<script src="app.js"></script>
可以避免阻塞 DOM 渲染。
使用 defer 或 async
<script src="app.js" defer></script>
<script src="sdk.js" async></script>
两者区别:
defer
- 不阻塞 DOM 解析
- DOM 解析完成后执行
- 多个 script 按顺序执行
async
- 异步下载
- 下载完成立即执行
- 执行顺序不保证
九、减少回流与重绘
在性能优化中,有两个非常重要的概念:
回流(Reflow)
重绘(Repaint)
其中:
回流一定会触发重绘,但重绘不一定会触发回流。
回流需要重新计算元素的位置和尺寸,因此性能开销较大。
常见触发回流的操作包括:
修改元素尺寸:
width
height
margin
padding
font-size
DOM 操作:
新增节点
删除节点
读取布局信息:
el.offsetHeight
el.offsetWidth
el.getBoundingClientRect()
这些操作都会导致浏览器重新计算布局。
十、GET 和 POST 的区别
在 HTTP 请求中最常见的两种方法是:
GET
POST
语义上
从 RESTful 语义来说:
GET 获取资源
POST 提交数据 / 新增资源
数据传输方式
GET 参数通常放在 URL 的 QueryString 中:
/api/getUser?id=123&name=zp
POST 数据通常放在 Request Body 中。
长度限制
GET 的 URL 一般限制在:
2KB ~ 8KB
POST 理论上没有限制(由服务器配置决定)。
安全性
GET 和 POST 本质上都是 明文传输,真正的安全性来自 HTTPS。
幂等性
HTTP 是无状态协议。
GET 是幂等的
POST 不是幂等的
缓存
浏览器默认:
GET 可以缓存
POST 一般不缓存
十一、一次 HTTP 请求包含哪些信息
一个 HTTP 请求通常包含三部分:
请求行
请求头
请求体
请求行
例如:
GET /api/getUser?id=123 HTTP/1.1
包含:
- 请求方法
- URL
- HTTP 版本
请求头
常见请求头包括:
Authorization
Cookie
Content-Type
User-Agent
请求体
请求体一般出现在:
POST
PUT
PATCH
用于提交数据,例如:
{
"name": "Tom",
"age": 20
}
十二、为什么需要 TCP 三次握手
在 HTTP 请求之前,需要先建立 TCP 连接。
TCP 连接需要通过 三次握手建立。
流程如下:
客户端 → 服务端
SYN
服务端 → 客户端
SYN + ACK
客户端 → 服务端
ACK
三次握手的核心目的,是 确认双方的发送能力和接收能力,从而建立可靠连接。
当连接建立完成后,客户端和服务器才会开始正式传输数据。
总结
从浏览器输入 URL 到页面展示,大致流程如下:
DNS解析
↓
TCP三次握手
↓
HTTP请求
↓
HTML下载
↓
HTML解析 → DOM Tree
↓
CSS解析 → CSSOM Tree
↓
Render Tree
↓
Layout(回流)
↓
Paint(绘制)
↓
Layer(图层)
↓
Composite(GPU合成)
↓
页面显示
理解这套流程不仅有助于掌握 浏览器底层原理,也能帮助我们在开发中进行 性能优化和问题排查。
在前端面试中,这也是一个非常经典且高频的基础问题。