上回书说到,彼时的页面,上有解析连天网,下有TCP断连诀,浏览器接收HTML,欲给页面画上朗朗乾坤,传说,浏览器在解析之时,曾言道...
注:本文为上一篇《从url输入到页面渲染:深挖浏览器渲染的完整过程》中,“浏览器解析与渲染页面”内容部分的详细解读,上一篇文章对本文内容无影响,可放心食用。
我获取内容,并以七步完成渲染:
第一步:解析HTML(HTML Parser),构建HTML树。
第二步:解析CSS(CSS Parser),构建CSSOM树。
第三步:合成渲染树(Render Tree)
第四步:布局(Layout)
第五步:分层(Layering)
第六步:绘制(Painting)
第七步:合成显示(Display)
过程详解如图所示:
接下来,是对于每一个步骤的详细分析:
第一步:解析HTML,构建DOM树
过程详解
请先看图:
当用户输入URL后,浏览器通过网络请求获取HTML文件(相关内容详解文章,点我打开链接)。
此时,HTML文件本质上是一段字节流,浏览器会根据HTTP头中的Content-Type(如text/html)和编码格式(如UTF-8)将其解码为可读的HTML字符串。
HTML解析器(HTML Parser)开始工作,将字符串解析为DOM树。解析过程中,浏览器会按以下步骤处理:
-
令牌化(Tokenization) :通过正则匹配标签名、属性、注释等内容,将HTML字符串拆分为一个个“令牌”(Token)。
例如: <div id="box"></div> 会被拆分为 <div> 、id="box" 、</div> 三个令牌。 -
节点构建(Tree Construction) :根据令牌生成对应的DOM节点对象。
每个节点包含类型(如
div)、属性(如id: "box")和子节点,最终不断递归,形成一个树状结构,称为DOM树 -
阻塞行为:解析HTML时,若遇到
<script>标签,浏览器会暂停解析,优先执行脚本(除非脚本添加了async或defer属性)。
关键点与优化
-
DOM树的高效性:DOM树以树形结构存储数据,支持快速查找和操作(如
querySelector)。 -
性能优化:减少HTML文件大小(压缩空白符、合并资源)、避免深层嵌套结构,可加速解析。
DOM节点内存结构
以HTML片段<div id="box"><p>Hello</p></div>为例,DOM树的存储形式如下:
{
nodeType: 1,
nodeName: "DIV",
nodeValue: null,
attributes: { id: "box" },
childNodes: [
{
nodeType: 1,
nodeName: "P",
nodeValue: null,
attributes: {},
childNodes: [
{
nodeType: 3,
nodeName: "#text",
nodeValue: "Hello"
}
],
parentNode: [指向div节点]
}
],
parentNode: null
}
第二步:解析CSS,构建CSSOM树
过程详解
请先看图
浏览器在解析HTML时,会同时下载CSS文件(通过<link>标签)。CSS文件同样需要经过解析,生成CSSOM树(CSS Object Model Tree)。
解析过程如下:
-
下载与解码:浏览器根据
Content-Type(如text/css)和编码格式(如UTF-8)解码CSS文本。 -
词法分析(Lexical Analysis) :将CSS文本拆分为令牌(如选择器、属性值)。
-
规则生成:将令牌转换为CSS规则对象(如
div { color: red })。 -
构建CSSOM树:将规则按选择器优先级(如
!important、ID选择器、类选择器)和继承关系组合成树状结构。
关键点与优化
-
CSSOM树的特性:样式规则存在继承和层叠(如
color: red继承至子元素),最终通过优先级计算确定每个元素的样式。 -
阻塞渲染:CSS解析会阻塞HTML解析和渲染(因为浏览器需要知道所有样式才能确定布局)。
-
优化建议:
- 将关键CSS内联到HTML中,减少请求。
- 使用
media属性延迟加载非关键CSS(如<link rel="stylesheet" media="print">)。
CSSOM节点内存结构
以CSS片段div.box { color: red }为例,其内存存储如下:
// CSS规则对象
{
selector: "div.box", // 选择器
specificity: [0, 1, 1], // 优先级计算(ID:0, 类:1, 元素:1)
declarations: {
color: "red", // 样式属性
... // 其他属性
},
next: null // 链表指针,指向下一个规则
}
第三步:合成渲染树(Render Tree)
过程详解
同样先看图再说:
DOM树和CSSOM树构建完成后,浏览器会将两者合并生成渲染树(Render Tree)。渲染树仅包含需要渲染的节点(如<div>、<span>),忽略不可见元素(如display: none的节点)。
合成过程如下:
-
匹配规则:将DOM节点与CSSOM规则匹配,确定每个节点的最终样式。
-
生成渲染节点:每个可见节点生成对应的渲染节点(Render Object),包含几何信息(如位置、大小)和样式属性。
-
层级关系:渲染树的结构与DOM树类似,但会根据
z-index、position等属性调整节点的层级。
关键点与优化
- 渲染树的轻量化:渲染树仅包含可见元素,减少计算开销。
- 性能影响:频繁修改DOM或CSSOM会导致渲染树频繁重建,触发重排(Reflow)。
第四步:布局(Layout)
过程详解
布局阶段(也称重排)的目标是计算每个元素在屏幕上的精确位置和大小。
浏览器会遍历渲染树,应用盒模型规则(如margin、padding、border),并结合文档流(Normal Flow)和定位(如float、absolute)确定元素的位置。
关键步骤:
-
根节点计算:从根节点(
<html>)开始,计算视口大小。 -
递归布局:依次计算子元素的位置和大小。例如:
div { width: 100px; height: 100px; margin: 10px; }浏览器会计算
div的实际宽度为100px + 10px*2 = 120px。 -
BFC与IFC:通过块级格式化上下文(BFC)和行内格式化上下文(IFC)管理元素的排列规则。
关键点与优化
-
重排的代价:布局是性能开销最大的阶段之一,频繁触发会导致页面卡顿。
-
优化建议:
- 避免频繁读取布局属性(如
offsetWidth),因为这会强制触发同步重排。 - 使用
transform或opacity进行动画,避免修改布局属性(如left、width)。
- 避免频繁读取布局属性(如
第五步:分层(Layering)
过程详解
分层阶段的目的是将页面划分为多个独立的图层(Layer),每个图层可以独立渲染,提升性能。浏览器会根据以下条件创建新图层:
- 透明元素(如
opacity < 1)。 - 3D变换(如
transform: translateZ(0))。 - 固定/绝对定位(如
position: fixed)。 z-index非0的堆叠上下文。
每个图层会生成一个独立的合成树(Composited Layer Tree),包含图层的层级关系和绘制顺序。
以下面浏览器的主页为例,在你刚打开浏览器的时候,你看到的页面可能和我类似,是这个样子:
然而,如果你打开浏览器的控制台页面(快捷键f12 或 鼠标右键点击检查),使用图层(layers)工具:
随后,你会看到,你看到的页面并不是一个平面图,而是由一个个小区快,通过布局一层层叠在页面上,最后才组成你所看到的页面:
关键点与优化
- GPU加速:独立图层由GPU加速绘制,减少CPU负担。
- 性能平衡:过多图层会增加内存消耗,需合理使用
will-change或transform属性。
第六步:绘制(Painting)
过程详解
绘制阶段将每个图层的内容转换为像素点,生成位图(Bitmap)。浏览器会遍历图层,按以下步骤绘制:
- 绘制顺序:从后往前绘制(遵循
z-index和堆叠上下文规则)。 - 像素操作:将样式属性(如
background-color、border-radius)转换为像素点。 - 光栅化:将矢量图形(如SVG)转换为位图,供GPU使用。
关键点与优化
-
重绘的代价:绘制操作涉及大量像素计算,频繁触发会导致性能下降。
-
优化建议:
- 使用硬件加速(如
transform)减少重绘。 - 避免使用复杂的CSS效果(如大量渐变或阴影)。
- 使用硬件加速(如
第七步:合成显示(Display)
过程详解
合成显示是最终的渲染阶段,浏览器将所有图层的位图合并,生成最终的屏幕图像。过程如下:
-
图层合并:浏览器的合成线程(Compositor Thread)将图层按
z-index顺序合并。 -
GPU渲染:合并后的位图通过GPU发送到显示器,按帧率(如60Hz)刷新屏幕。
-
防闪烁处理:通过双缓冲(Double Buffering) 技术,避免绘制过程中的画面撕裂。
关键点与优化
-
帧率控制:确保每帧绘制时间不超过16ms(60Hz屏幕)。
-
动画优化:使用
requestAnimationFrame确保动画与屏幕刷新率同步。
隐藏关卡:回流(Reflow)与重绘(Repaint)
过程详解
- 回流/重排(Reflow)
-
当DOM树或CSSOM树发生变化,导致元素的几何属性(如位置、大小)改变时,浏览器需重新计算布局(Layout),这一过程称为回流。
触发回流后,浏览器会重新进行布局 、分层 、绘制、合成显示 等步骤。
-
触发条件:
- 修改元素的几何属性(如
width、height、margin、padding)。 - 添加/删除可见的DOM节点。
- 调整窗口大小(
resize)或滚动页面(scroll)。 - 读取某些布局属性(如
offsetWidth、getBoundingClientRect),强制触发同步回流。
- 修改元素的几何属性(如
- 重绘(Repaint)
-
当元素的样式属性(如颜色、背景、透明度)改变但不影响布局时,浏览器需重新绘制像素,这一过程称为重绘。
触发重绘后,则仅需重新执行绘制 和 合成显示 步骤,但仍需依赖布局和分层的结果。
-
触发条件:
- 修改非几何属性(如
color、background-color、visibility)。 - 激活伪类(如
:hover)。
- 修改非几何属性(如
本文为作者的理解,如果内容有误,欢迎各位读者在评论区指正。
如果觉得这篇文章对你有所帮助,不妨动动小手,点赞 + 收藏 一波!🌟