浏览器渲染机制 + HTTP 请求解析(前端面试高频)

6 阅读7分钟

在前端面试中,经常会遇到一个经典问题:

浏览器输入一个 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 TreeCSSOM 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)

例如以下属性可能会创建新的合成层:

  • transform
  • opacity
  • position: 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合成)
↓
页面显示

理解这套流程不仅有助于掌握 浏览器底层原理,也能帮助我们在开发中进行 性能优化和问题排查

在前端面试中,这也是一个非常经典且高频的基础问题。