浏览器渲染
关键渲染路径(CRP)
浏览器渲染网页是一个复杂的过程,这个过程涉及关键渲染路径(CRP)。CRP 包含从获取 HTML、CSS 和 JavaScript 资源开始,到最终将像素呈现在屏幕上的一系列关键步骤,这些步骤包括解析 HTML、解析 CSS、布局以及绘制等多个环节,
MDN上的描述
浏览器开始解析 HTML,将收到的数据转换为 DOM 树。当浏览器每次发现 DOM 树包含外部资源就会初始化其请求(无论是样式、脚本或者嵌入的图片引用)。有时这些请求会阻塞,意味着解析剩下的 HTML 会被终止,直到前面这些重要的资源得到处理。浏览器接着解析 HTML,发送请求并构造 DOM,直到 HTML 的文件结尾,此时就会开始构造 CSSOM。等到 DOM 和 CSSOM 完成之后,浏览器构造渲染树,计算所有可见内容的样式。一旦渲染树完成,布局就会开始,定义所有渲染树元素的位置和大小。在布局完成后,页面被渲染完成(被绘制到屏幕上)。
浏览器渲染流程
得到HTML
浏览器接收到HTTP响应的数据包,这些数据包包含HTML,CSS,JavaScript以及可能的图片和其他资源。
解析HTML
意义:
- 由于字符串难以进行操作,浏览器首先会将 HTML 字符串解析成 DOM 树和 CSSOM 树这种容易操作的对象结构,也提供了 JS 操作这两颗树的能力。
- 构建 CSSOM 树主要是使得浏览器能够更高效地理解和处理样式之间的关系。
实现:
- 构建 DOM 树:浏览器首先会解析 HTML 文档,构建一个 DOM(文档对象模型)树。每个 HTML 元素都会成为树中的一个节点。
- 构建 CSSOM 树:DOM 树构建完成一部分后开始构建 CSSOM 树,当浏览器开始解析 HTML 时,它也会同时发现并请求 CSS 资源。一旦 CSS 文件开始被接收并解析,或者当遇到内联 CSS 时,浏览器就会开始构建 CSSOM 树。
解析CSS(样式计算)
意义:
- 样式控制和动态更新:
- JavaScript 可以通过操作 CSSOM 树来动态修改元素的样式。
- 渲染树合成基础:
- CSS 计算得到的每个元素的样式信息是构建渲染树的重要基础。
过程:
- 样式收集和处理:
- 浏览器首先收集来自不同来源的样式信息,包括内联样式
<style>
标签内的样式以及外部 CSS 文件。 - 对于
<style>
标签内的样式和外部 CSS 文件,浏览器会按照它们在 HTML 文档中的加载顺序进行解析
- 浏览器首先收集来自不同来源的样式信息,包括内联样式
- 标记化和语法分析
- 对收集到的 CSS 样式内容进行标记化,将其分解成一个个的标记;
- 构建 CSSOM 树
- 浏览器根据 CSS 的层叠规则来计算每个元素最终的样式
- 依次为树中的每个节点计算出它最终的样式,称之为 Computed Style
注意:CSS 是渲染阻塞的:浏览器会阻塞页面渲染直到它接收和执行了所有的 CSS。CSS 是渲染阻塞是因为规则可以被覆盖,所以直至 CSSOM 构建完成之前,内容都不能被渲染。
布局
意义:
- 渲染树的构建是将 DOM 树和 CSSOM 树结合的过程,
- 布局取决于屏幕的尺寸。布局这个步骤决定了在哪里和如何在页面上放置元素,决定了每个元素的宽和高,以及他们之间的相关性。
过程:
- 生成布局树(渲染树)
- 浏览器检查每个节点,从 DOM 树的根节点开始,并且决定哪些 CSS 规则被添加。渲染树只包含了可见内容。 在DOM树上不可见的元素,最后都不会出现在布局树上。
- 计算布局(回流):
- 一旦渲染树构建完成,浏览器会计算每个节点的几何信息,包括位置和大小,这个过程称为布局(或回流)。
分层(绘制)
意义:
- 分层的好处在于,将来某一个层改变后,仅会对该层进行后续处理,只需要更新特定的层,而不是整个页面,减少了重绘的范围。
过程:
- 对渲染树进行分层:
- 渲染主线程会使用一套复杂的策略对整个布局树(渲染树)进行分层。例如,具有 3D 变换、视频元素或者使用了硬件加速的元素通常会被单独分层。
- 生成绘制列表:
- 渲染主线程会根据计算好的样式和布局信息,为每个图层单独生成绘制指令集,这些绘制指令会告诉浏览器的图形处理单元(GPU)如何将元素绘制到屏幕上。
分块
意义:
- 分块就是将页面划分成多个小的矩形区域(块),如果一次性处理整个页面的绘制,会消耗大量的系统资源并且可能导致性能问题。分块就是将页面划分减少绘制时的消耗。
过程:
- 浏览器(合成线程)对每个图层进行分块,根据布局信息来确定哪些块是当前可见的或者即将可见的(考虑到滚动等情况)。对于每个块,会独立地进行绘制操作。
光栅化
意义:
- 光栅化就是将矢量图形(如由 HTML 和 CSS 定义的各种页面元素形状)转换为位图(由像素组成的图像)的过程,这是为了让图形能够在以像素为基础的显示器上正确显示。
过程:
- 确定光栅化区域:
- 浏览器首先需要确定哪些区域的网页元素需要进行光栅化。这通常是基于布局和分层信息来判断的。
- 矢量图形转换为位图:
- 对于需要光栅化的元素,浏览器将其矢量图形表示(如根据 CSS 样式定义的形状、文本的字体轮廓等)按照一定的算法转换为位图。
- 像素数据处理和优化:
合成
意义:
- 当文档的各个部分以不同的层绘制,相互重叠时,必须进行合成,以确保它们以正确的顺序绘制到屏幕上,并正确显示内容。
- 实现高效更新和动画效果:
- 某些元素的变化可能只涉及到特定的层,通过合成,浏览器可以只更新受影响的层。
实现:
- 生成合成指令
- 合成线程根据每个层的信息(包括层的位置、大小、透明度等)以及它们之间的关系,生成一系列的合成指令。这些指令类似于一个 “蓝图”,告诉浏览器如何将各个层的内容进行组合和绘制。
- 提交给 GPU 进行处理:
- 合成线程将生成的合成指令提交给 GPU 进程。GPU 进程接收到指令后,会根据指令中的信息进行具体的图形处理操作。
- 完成屏幕成像:
- GPU 完成合成操作后,会产生系统调用,将合成后的图像数据提交给 GPU 硬件,最终完成在屏幕上的成像显示。
各个步骤输入输出
每个阶段都有明确的输入输出,上一个阶段的输出会成为下一个阶段的输入。
输入 | 输出 | |
---|---|---|
解析 HTML 与 CSS | HTML 文档内容(字节) | DOM 树 |
样式计算 | DOM 树 | DOM 树和 CSSOM 树 |
布局 | DOM 树和 CSSOM 树 | 渲染树 |
分层 | 渲染树 | 分层后的结构 |
分块 | 分层后的结构 | 矢量图形信息 |
光栅化 | 矢量图形信息 | 每个块的位图(像素矩阵) |
合成 | 位图数据 | 最终合成后的图像数据 |