渲染流程: 构建 DOM 树、样式计算、布局阶段、分层、绘制、光栅化 和 合成。
构建 DOM 树
这是因为浏览器无法直接理解和使用 HTML,所以需要将 HTML 转换为 浏览器能够理解的结构—— DOM 树。
样式计算
当渲染引擎接收到 CSS 文本时,会执行⼀个转换操作,将 CSS 文本转换为浏览器可以理解的结构—— styleSheets;
css 的来源只要有三种:通过link引用的外部 CSS 文件;<style > 标签内的 CSS;元素的 style 属性内嵌的 CSS。
样式计算的目的是为了计算出 DOM 节点中每个元素的具体样式,这个阶段可以分为三步来完成:
1. 把 CSS 转换为浏览器能够理解的结构;
2. 转换样式表中的属性值,使其标准化, 比如 2em 解析文 32px, color:green 解析为 rpg;
3. 计算出DOM树中每个节点的具体样式;
这就涉及到 CSS 的 继承规则 和 层叠规则 了
继承规则 :CSS 继承就是每个 DOM 节点都包含有父节点的样式;
层叠规则:层叠是 CSS 的⼀个基本特征,它是⼀个定义了如何合 并来自多个源的属性值的算法。它在 CSS 处于核心地位,CSS 的全称“层叠样式表”正是强调了这 ⼀点。
布局阶段
我们有 DOM 树和 DOM 树中元素的样式,接下来就需要计算出 DOM 树中 可见元素 的几何位置,我们把这个计算过程叫做布局。
Chrome 在布局阶段需要完成两个任务:创建布局树 和 布局计算。
创建布局树的过程:
遍历DOM树中的所有 可见节点,并把这些节点加到布局中;
而不可见的节点会被布局树忽略掉,如 head 标签下面的全部内容, body.p.span 这个元素,因为它的属性包含 dispaly:none,所以这个元素也没有被包进布局树。
分层
页⾯中有很多复杂的效果,如⼀些复杂的3D变换、页面滚动,或者使用 z-indexing 做 z 轴排序等,为了更加方便地实现这些效果,渲染引擎还需要为特定的节点生成专用的图层,并生成⼀棵对应的图层树(LayerTree)。
浏览器的页面实际上被分成了很多图层,这些图层叠加后合成了最终的页面。
并不是布局树的每个节点都包含⼀个图层,如果⼀个节点没有对应的层,那么这个节点就从属于父节点的图层。
如何创建层?
1 拥有层叠上下⽂属性的元素会被提升为单独的⼀层, 比如 明确定位属性的元素(position)、定义透明属性的元素、使⽤CSS滤镜的元素等,参考层叠上下文;
2 需要剪裁(clip)的地方也会被创建为图层。
图像绘制
渲染引擎实现图层的绘制会把一个图层的绘制拆分成很多小的绘制指令,然后再把这些指令按照顺序组成⼀个待绘制列表。
我们可以通过开发工具来查看。
光栅化
绘制列表只是用来记录绘制顺序和绘制指令的列表,而实际上绘制操作是由渲染引擎中的合成线程来完成的。
当图层的绘制列表准备好之后,主线程会把该绘制列表提交(commit)给合成线程。
合成线程 会按照 视口(用户能看到的那部分叫视口 viewport) 附近的图块(合成线程会将图层划分为图块 tile)来优先生成位图,实际生成位图的操作是由栅格化来执行的。所谓栅格化是指将图块转换为位图,简单来说就是我们需要做的是把这些信息转化为 显示器中的像素,这个转化的过程,叫做光栅化。
我们要绘制一个页面,最简单的做法是只光栅化视口内(viewport)的网页内容,如果用户进行了页面滚动,就移动光栅帧(rastered frame)并且光栅化更多的内容以补上页面缺失的部分。(我们没有必要一开始就全部 绘制所有的内容,产生开销太大,影响性能)。
合成和显示
一旦所有图块都被光栅化,合成线程就会生成一个绘制图块的命令--- DrawQuad 命令,然后将改命令提交给浏览器进程。
浏览器进程里面有一个叫 viz 的组件, 用来接受合成线程发过来 DrawQuad 命令,然后根据 DrawQuad 命令, 将其页面内容绘制到内存中, 最后在将内存显示在屏幕上。
完整的流程
1. 渲染进程将HTML内容转换为能够读懂的 DOM 树结构。
2. 渲染引擎将CSS样式表转化为浏览器可以理解的 styleSheets,计算出DOM节点的样式。
3. 创建布局树,并计算元素的布局信息。
4. 对布局树进行分层,并生成分层树。
5. 为每个图层生成绘制列表,并将其提交到合成线程。
6. 合成线程将图层分成图块,并在光栅化线程池中将图块转换成位图。
7. 合成线程发送绘制图块命令 DrawQuad 给浏览器进程。
8. 浏览器进程根据 DrawQuad 消息生成页⾯,并显示到显示器上。
重排、重绘 和 合成
更新了元素的几何属性(例如改变元素的宽度、高度等)-----> 重排
重排需要更新完整的渲染流⽔线,所以开销也是最大的
更新了元素的绘制属性(比如更改了某些元素的背景颜色)-----> 重绘
如果修改了元素的背景颜⾊,那么布局阶段将不会被执行,因为并没有引起⼏何位置的变换,所以就直接进⼊了绘制阶段,然后执⾏之后的⼀系列⼦阶段,这个过程就叫重绘。相较于重排操作,重绘省去了布局和分层阶段,所以执行效率会⽐重排操作要高⼀些。
直接合成阶段
如果更改⼀个既不要布局也不要绘制的属性,渲染引擎将跳过布局和绘制,只执行后续的合成操作,我们把这个过程叫做合成。
我们使用了 CSS 的 transform 来实现动画效果,这可以避开重排和重绘阶段,直接在非主线程上执行合成动画操作。这样的效率是最高的,因为是在非主线程上合成,并没有占用主线程的资源,另外也避开了布局和绘制两个子阶段,所以相对于重绘和重排,合成能大大提升绘制效率。
引用资料
浏览器工作原理与实践