渲染主流程
主流程分为五个阶段
- 解析阶段
- 构建渲染树阶段
- 布局阶段
- 绘制阶段
- 渲染阶段
解析阶段
分为两个独立部分:HTML解析、样式解析
HTML解析(HTML->DOM)
<!doctype HTML>
<html>
<head>
<meta charset="UTF-8"/>
<title>My simple page</title>
<link rel="stylesheet" src="styles.css"/>
<script src="myscript.js"></script>
</head>
<body>
<h1 class="heading">My Page</h1>
<p>A paragraph with a <a href="https://example.com/about">link</a></p>
<div>
<img src="myimage.jpg" alt="image description"/>
</div>
<script src="anotherscript.js"></script>
</body>
</html>
HTML解析是顺序的,从第一行代码片开始,逐一解析。解析过程中遇到外联资源(image、iframe、link、script)会立即请求对应的资源。
这些资源简单分为阻塞性和非阻塞性两种:
- 阻塞性:当前HTML解析停止,拉取对应资源并立即解析。完成之后再重新回到HTML解析
- 内联javascript资源
- 外联普通javascript资源
- 外联defer javascript资源
- 内联css资源
- 非阻塞性:不影响当前HTML解析
- image
- iframe
- 外联async javascript
样式解析(CSS->CSSOM)
构建CSSOM树与构建DOM树相似,其中CSSOM树与DOM树结构相似但并不相同。
如下图所示:CSS会根据用户CSS文件来解析出叠层样式,其解析单位是已选择器来的,可以是元素标签、class以及id,最终得到单位的样式描述值。例:.error class选择器,得到描述值color: red。
构建渲染树阶段(DOM+CSSOM->Render Tree)
Render树通过DOM和CSSOM相结合形成。
需要注意的是,DOM树结构和CSSOM树形结构并不相同,Render树和DOM树相同。在DOM和CSSOM相结合时,以DOM树根节点为基准,遍历CSSOM树并最终获得每一个dom节点的叠层样式。
需要注意,CSSOM中样式并不完全,它是内联样式+外联样式+用户自定义样式+浏览器默认样式共同构成。如用户在p标签设置了font-size值,但没有设置color,此时浏览器会逐一通过优先级来获取具体的值。
布局阶段(layout)
此过程能回得到第四个树:呈现树(包含渲染树的样式以及本步骤得到的集合数据)。 也叫重排。目的是获取每一个元素在浏览器视口的几何数据(位置、大小等)。由于浏览器默认为流式布局,排在前面或者上面已经定位的元素,所以布局是从上到下,从左到右依次解析。
布局同上遵守以下模式:
- 父呈现器确定自己的宽度
- 父呈现器依次处理子呈现器
- 放置子呈现器(设置x、y坐标)
- 父呈现器下所有子呈现器处理完毕之后,根据子呈现器的累加高度、边距和补白的高度来设置自身高度。(2、3、4是一个递归的过程,直到最末级)
布局分为两种模式:
- 全局:重新布局整个渲染树,重新获取完整的呈现树。
- 增量:不会重新布局,仅修改部分以及部分受影响部分重新布局。
绘制阶段(paint)
根据呈现树规则,遍历并调用呈现器的paint方法,将呈现器的内容显示的屏幕上。和布局相同,分为两个部分,全局会重新绘制整个屏幕显示;增量只需重新绘制修改部分。
块呈现器堆栈顺序:
- 背景颜色
- 背景图片
- 边框
- 子代元素
- 轮廓
渲染阶段(display)
把文档的结构、元素的样式、几何形状和绘制顺序转换为屏幕上的像素称为光栅化。合成是一种将页面的各个部分分层,分别栅格化,并在一个被称为合成器线程的独立线程中合成为页面的技术
一旦创建了层树并确定了绘制顺序,主线程就会将该信息提交给合成器线程。 合成器线程然后栅格化每个图层。 一个图层可能像页面的整个长度一样大,因此合成器线程会将它们分成图块,并将每个图块发送到光栅线程(Raster)。 栅格线程栅格化每一个瓦片(tile)并将它们存储在GPU内存中。
通过IPC将合成器帧提交给浏览器进程。这时可以从UI线程添加另一个合成器帧以用于浏览器UI更改,或者从其他渲染器进程添加扩充数据。 这些合成器帧被发送到GPU用来在屏幕上显示。 如果发生滚动事件,合成器线程会创建另一个合成器帧并发送到GPU。
合成的好处是它可以在不涉及主线程的情况下完成。 合成线程不需要等待样式计算或 JavaScript 执行。 这就是合成动画是平滑性能的最佳选择的原因。 如果需要再次计算布局或绘图,则必须涉及主线程。
参考: