【浏览器原理-4】浏览器的页面渲染

ObjectKaz Lv4

课程链接:https://time.geekbang.org/column/intro/100033601?tab=catalog

渲染的整体流程

渲染流程是从 HTML 到页面的流程。由于它的机制太过于复杂,我们把这样的一个处理流程叫做渲染流水线。按照时间顺序,我们把渲染流水线大致分为:

  1. 构建 DOM 树
  2. 样式计算
  3. 布局
  4. 分层
  5. 绘制
  6. 分块
  7. 光栅化
  8. 合成

构建 DOM 树

服务器返回的 HTML 是一段字符串,浏览器是无法理解和使用的。这时候,需要将 HTML 转换成浏览器能够理解的结构——DOM 树。

样式计算

和 HTML 一样,CSS 也是无法被浏览器直接理解的,所以首先需要将 CSS 文本转换为浏览器可以理解的结构——styleSheets。

控制台输入 document.styleSheets 可以查看它的结构

此外,由于 CSS 存在各种不同的单位,如 pxemrem%等,为了方便浏览器解析和处理,需要将这些单位转换成统一的单位,这个过程称为 标准化

接下来,需要算出每个 DOM 节点的样式属性,计算过程需要遵守 CSS 的继承规则和层叠规则。

布局

浏览器执行到这一步时,已经拥有了 DOM 结构以及各个节点的样式。由于 DOM 结构上面有很多不显示的元素,如 head 标签和含有 display: none 样式的元素。为了方便绘制界面,需要将这些不显示的元素删掉,这就又创建了一颗 布局树

创建好布局树后,浏览器便会对布局树上所有的节点进行布局计算。计算结果会重新写入布局树中。

这里并没有严格区分输入和输出与。Chrome 团队正在重构布局代码,下一代布局系统叫 LayoutNG,试图更清晰地分离输入和输出,从而让新设计的布局算法更加简单。

分层

由于浏览器页面具有复杂的效果,如页面滚动和一些复杂的 3D 效果,以及 z-index带来的一些层叠效果。所以,浏览器还需要为一些特殊的节点生成专用的图层,并生成对应的图层树。浏览器最后展示的页面,是多个图层叠加的结果。

Chrome Devtools 可以通过“图层”选项卡来浏览网页中的图层

需要注意的是,不是布局树的每个节点都包含一个图层,如果一个节点没有对应的层,那么这个节点就从属于父节点的图层。

  1. 拥有层叠上下文属性的元素会被提升为单独的一层.明确定位属性的元素、定义透明属性的元素、使用 CSS 滤镜的元素等,都拥有层叠上下文属性。
  2. 第二点,需要剪裁(clip)的地方也会被创建为图层。(例如限定文本宽高然后设置 overflow: auto;,这种情况会为文字创建一个单独的层,如果有滚动条,滚动条也会成为一个单独的层)

MDN(层叠上下文):https://developer.mozilla.org/zh-CN/docs/Web/CSS/CSS_Positioning/Understanding_z_index/The_stacking_context

绘制

构建完图层树之后,渲染引擎会对图层树中的每个图层进行绘制。为了方便,渲染引擎会把图层的绘制分成小的绘制指令(如 绘制正方形等)。

在 Chrome Devtools 中的 Layers 也可以看到具体的绘制指令的一些信息。

光栅化

绘制列表只是用来记录绘制顺序和绘制指令的列表,而实际上绘制操作是由渲染引擎中的合成线程来完成的。当图层的绘制列表准备好之后,主线程会把该绘制列表提交(commit)给合成线程。

通常,一个页面是很大的,用户只能看见一部分,用户能看见的这一部分称为 视口(Viewport)。所以,一开始就绘制整个页面是比较浪费的。

此时,合成线程会将图层划分为图块(tile),这些图块的大小通常是 256x256 或者 512x512。

然后合成线程会优先生成视口附近的位图。

所谓栅格化,是指将图块转换为位图,而图块是栅格化执行的最小单位。渲染进程维护了一个栅格化的线程池,所有的图块栅格化都是在线程池内执行的。

栅格化过程都会使用 GPU 来加速生成,使用 GPU 生成位图的过程叫快速栅格化,或者 GPU 栅格化,生成的位图被保存在 GPU 内存中。GPU 操作是是在专门的 GPU 进程中完成的。

合成

一旦所有图块都被光栅化,合成线程就会生成一个绘制图块的命令—— DrawQuad,然后将该命令提交给浏览器进程。

浏览器进程里面有一个叫 viz 的组件,用来接收合成线程发过来的 DrawQuad 命令,然后根据 DrawQuad 命令,将其页面内容绘制到内存中,最后再将内存的页面内容显示在屏幕上。

渲染流程小结

阶段输入处理产物
构建 DOM 树HTML 字符串转换DOM 树
样式计算CSS生成 styleSheets、标准化、计算各个节点的样式各个 DOM 节点的样式
布局DOM 树去除不显示的元素布局树
分层布局树针对特殊的元素进行分层分层树
绘制分层树生成绘制指令绘制列表
分块绘制列表合成线程将图层分成图块图块
光栅化图块在光栅化线程池中将图块转换成位图位图
合成位图浏览器进程根据 DrawQuad 消息生成页面,并显示到显示器上页面

浏览器页面的更新

重排

如果通过 JavaScript 或者 CSS 修改元素的几何位置属性(元素的宽度、高度),那么浏览器会触发重新布局,解析之后的一系列子阶段,这个过程就叫重排

重绘

如果只是单纯的修改了元素的背景颜色,那么布局阶段会被跳过,直接进入了绘制阶段,然后执行之后的一系列子阶段,这个过程就叫重绘

直接合成阶段

如果使用 CSS 的 transform 来实现动画效果,就可以避开重排和重绘阶段,直接在非主线程上执行分块、栅格化、合成等操作。

性能的影响

很明显,重排最耗费性能,重绘其次,直接合成是性能影响最小的。

  • 标题: 【浏览器原理-4】浏览器的页面渲染
  • 作者: ObjectKaz
  • 创建于: 2021-12-22 14:27:36
  • 更新于: 2021-12-27 14:34:39
  • 链接: https://www.objectkaz.cn/89a86982d1ff.html
  • 版权声明: 本文章采用 CC BY-NC-SA 4.0 进行许可。