渲染流程概述
渲染的目标:将HTML文本转化为可以看到的像素点
当浏览器的网络线程收到 HTML 文档后,会产生一个渲染任务,并将其传递给渲染主线程
的消息队列。在事件循环机制
的作用下,渲染主线程取出消息队列中的渲染任务,开启渲染流程。
渲染流程的阶段(8个步骤):
HTML
解析- 样式计算
- 布局
- 分层
- 绘制
- 分块
- 光栅化
- 画
每个阶段的输出将成为下一个阶段的输入,整个流程形成了一套高效的生产流水线。
1. 解析 HTML
第一步: 解析 HTML
- HTML 解析: 浏览器会解析 HTML 文件,遇到
CSS
会解析CSS
,遇到JS
会执行JS
。 - 外部资源的加载: 浏览器会启动一个预解析线程,提前下载外部的
CSS
和JS
文件。
关键点:
link
标签会在主线程解析到时,继续解析 HTML,不会阻塞。
script
标签会暂停 HTML 解析,等待 JavaScript 下载并执行完成后,才能继续解析。
解析完成后,浏览器得到 DOM 树 和 CSSOM 树。
2. 样式计算
第二步: 样式计算
主线程会遍历 DOM 树,计算每个节点的最终样式,称之为 Computed Style。
这一过程存在转换操作,预设值(例如
red
)转换成绝对值(例如rgb(255, 0, 0)
),单位(例如em
)转化为px
。
此步骤后,我们得到了一棵包含样式的 DOM 树。
3. 布局
第三步: 布局
布局阶段,浏览器会根据 DOM 树计算出每个节点的几何信息,如宽高、位置等。
布局树的特性(DOM树和Layout树不一定是一一对应的):
display: none
的节点不会出现在布局树中。- 伪元素
::before
会出现在布局树中,尽管 DOM 树中没有。
完成布局后,浏览器会生成 布局树。
4. 分层
第四步: 分层
主线程会根据布局树,按照一套复杂的策略进行分层。
为什么要分层:
如果某一层发生变化,后续只处理这一层,从而提升性能。
影响分层的因素包括:滚动条、transform
、opacity
等,也可以通过will-change
属性更大程度的影响分层结果。
5. 绘制
第五步: 绘制
每一层的绘制指令集
会被生成,用于描述图层如何呈现内容。
完成绘制后,主线程将绘制信息提交给 合成线程,剩下的工作由合成线程完成。
6. 分块
第六步: 分块
合成线程将每一层分成多个小块,划分为更小的区域。
这一步会从线程池中取出多个线程来完成分块的工作。
7. 光栅化
第七步: 光栅化
合成线程将分块信息交给 GPU 进程,通过多个线程快速处理,优先处理靠近视口的区域。光栅化后的结果是每个块的位图。
光栅化的过程中会启动GPU进程进行加速。
8. 画
最后一步: 画
合成线程生成 指引(quad)信息,描述每个位图如何绘制到屏幕的正确位置,并考虑旋转、缩放等变形。
变形发生在合成线程,与渲染主线程无关,正是因为如此,
transform
样式非常高效。
最终,合成线程将指引信息提交给 GPU 进程,通过 GPU 硬件渲染生成屏幕上的图像。
总结
通过并行处理,浏览器能够在多个线程中同时完成渲染任务,保证页面能够快速响应用户交互,提高了整体性能。渲染流程的各个阶段和线程池的协作使得浏览器能够高效地绘制出用户所看到的页面,优化了体验和性能。
加速技巧:
will-change
属性可以提前告知浏览器需要优化的渲染层。- 使用
transform
和opacity
来避免页面重排,提高渲染效率。