前言
当你在浏览器地址栏输入一个网址并按下回车时,一场复杂的转换就悄然开始了。服务器返回的HTML文档只是一系列文本标记,而浏览器则承担了将这些抽象符号转化为可视化页面的重任。这个转换过程被称为"渲染",它涉及解析、构建、布局和绘制等多个精密步骤,每个环节都至关重要。
让我们一同探索浏览器如何将抽象的HTML标签转化为我们所见所闻的丰富网页体验,揭开这一日常数字魔术背后的技术面纱。
本文是参考HTML 渲染那些事儿所著,有需要的小伙伴可以去瞅瞅🚀🚀
页面渲染流程深度解析
页面的基本渲染流程图是:
1. DOM树的构建
浏览器接收到HTML文档后,会经过以下几个步骤构建DOM树:
- 字节转换:网络层传输的字节数据根据编码(如UTF-8)转换为HTML字符串
- 令牌化:将HTML字符串解析为令牌(标签名、属性等)
- 节点对象创建:将令牌转换为DOM节点对象
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<div id = "box">
<p>Hello , <span>欢迎</span> 来到我的世界 </p>
</div>
</body>
</html>
上面的HTML对应的DOM节点对象就是下面这种
// HTML元素对应的DOM节点对象表示
{
type: 'div',
attrs: {
id: 'box'
},
children: [
{
type: 'p',
children: [
{
type: 'span'
}
]
}
]
}
- 构造 DomTree:构造文档对象模型的最后一步就是构建 DomTree。
生成的DomTree如下所示:
DOM树采用树形数据结构,使得查找和操作非常高效,为后续渲染提供了基础。
2. CSSOM树的构建
与DOM树并行,浏览器会解析CSS样式表,构建CSS对象模型(CSSOM)树。CSSOM包含了所有CSS规则及其层级关系。
这里我了解到的信息是页面加载link外部的css样式的时候,这个时候是不会阻塞DOM Parse的。 它可以一边加载style样式,一边解析DOM,也就是所谓的并行。
但是加载完style样式也需要解析css样式,也就是生成cssom的时候,这个也需要在主进程里面进行,这样就会抢占主线资源,也就是parse css 和 parse html 并不能同时进行。
那为什么css也需要树状结构,因为 Css 的规则是支持“向下级联”的嵌套方案的,也就是我们在日常开发中 Css 的继承特性
3. 渲染树(Render Tree)的生成
DOM树和CSSOM树结合生成渲染树,渲染树只包含需要显示的节点及其样式信息。
这里要注意:不可见元素并不代表不能被看到的元素。比如 visibility: hidden
不同于 display: none
. 前者使元素不可见,但元素在布局中仍然占据空间(渲染为空框),而后者display: none
表示将元素从渲染树中完全移除,使元素不可见从而不是布局的一部分。
4. 布局(Layout / Reflow)
布局(也称为 Reflow)是浏览器计算 DOM 元素的几何信息(位置、大小等)的过程。
关键步骤:
-
盒子模型计算
- 浏览器根据 CSS 计算每个元素的
width
、height
、padding
、border
、margin
等。 - 例如,
box-sizing: border-box
会影响width
的计算方式。
- 浏览器根据 CSS 计算每个元素的
-
文档流布局
- 正常流(Normal Flow) :块级元素垂直排列,行内元素水平排列。
- 浮动(Float) :脱离正常流,其他元素环绕它布局。
- 绝对定位(Absolute/Fixed) :脱离文档流,相对于最近的非
static
父元素或视口定位。
-
BFC(块级格式化上下文)
- 触发条件:
overflow: hidden
、float
、position: absolute/fixed
、display: inline-block
等。 - 作用:防止外边距折叠(margin collapse),隔离浮动元素的影响。
- 触发条件:
-
Flexbox / Grid 布局
- 现代布局方式,浏览器需要计算
flex
或grid
容器的子元素排列方式。
- 现代布局方式,浏览器需要计算
触发布局(Reflow)的情况:
- 修改 DOM 结构(增删节点)
- 改变元素尺寸(
width
、height
、padding
、margin
) - 改变窗口大小(
resize
事件) - 读取某些布局属性(如
offsetWidth
、offsetHeight
)
优化建议:
- 避免频繁修改样式,尽量使用
transform
或opacity
(不触发 Reflow)。 - 使用
requestAnimationFrame
批量修改 DOM。
5. 图层(Layers)处理
浏览器会将某些元素提升为 独立的合成层(Compositing Layer) ,由 GPU 加速渲染,提高性能。
哪些情况会创建新图层?
-
transform: translate3d()
/transform: translateZ(0)
- 强制 GPU 加速,常用于优化动画性能。
-
position: fixed
/position: absolute
+z-index
- 固定定位或绝对定位的元素可能被提升为独立层。
-
opacity
< 1- 半透明元素可能被单独合成。
-
will-change: transform/opacity
- 提前告诉浏览器该元素可能会变化,优化渲染。
-
video
/canvas
/iframe
- 浏览器默认会为这些元素创建独立层。
图层的作用:
- 减少重绘(Repaint) :修改某个图层时,不影响其他图层。
- GPU 加速:某些变换(如
transform
、opacity
)由 GPU 处理,比 CPU 渲染更快。
优化建议:
- 合理使用
transform
和opacity
做动画,减少width
/height
变化。 - 避免滥用
z-index
,防止层爆炸(太多图层消耗内存)。
6. 绘制(Painting)
绘制阶段将布局计算后的元素转换成屏幕上的像素。
关键步骤:
-
生成绘制指令(Paint Records)
- 浏览器将元素分解为绘制命令(如“画矩形”、“填充颜色”)。
-
光栅化(Rasterization)
- 将矢量图形(如 CSS 形状)转换成位图(像素)。
- 通常由 GPU 加速,特别是
transform
和opacity
变化时。
-
合成(Compositing)
- 将所有图层(Layers)按
z-index
顺序合并成最终图像。
- 将所有图层(Layers)按
触发重绘(Repaint)的情况:
- 修改颜色(
color
、background-color
) - 修改边框样式(
border-style
) - 修改阴影(
box-shadow
) - 修改
opacity
(不触发 Reflow,但会 Repaint)
优化建议:
- 使用
transform
和opacity
做动画,避免触发重绘。 - 减少复杂的 CSS 选择器,提高绘制效率。
结论
通过深入分析浏览器渲染流程,我们可以清晰地看到,从输入URL到页面呈现是一个由多个精密环节构成的复杂过程。这个过程就像一场精心编排的交响乐,每个步骤都至关重要且环环相扣:
- 解析与构建阶段:DOM树和CSSOM树的并行构建展现了浏览器的高效处理能力,而渲染树的生成则体现了样式与结构的完美融合。
- 布局计算阶段:浏览器通过复杂的几何计算确定每个元素的精确位置和尺寸,各种布局模式(如Flexbox、Grid)的引入极大地丰富了网页布局的可能性。
- 渲染优化阶段:图层处理和GPU加速技术使得现代网页能够实现流畅的动画效果,同时保持高性能。
- 绘制呈现阶段:最终通过光栅化和合成将抽象的代码转化为用户可见的像素,完成从数字信息到视觉体验的转换。
这场"数字魔术"的背后,是浏览器工程师们数十年的技术积累和创新。随着Web技术的不断发展,浏览器渲染流程仍在持续优化,为开发者提供更强大的能力,为用户带来更流畅的体验。