渲染流程(浏览器工作原理学习-课5)

350 阅读5分钟

渲染流程

  1. 构建 DOM 树

    1. 树这种结构非常像我们现实生活中的“树”,其中每个点我们称为节点,相连的节点称为父子节点

  1. 样式计算

    1. 把 CSS 转换为浏览器能够理解的结构 -> 当渲染引擎接收到 CSS 文本时,会执行一个转换操作,将 CSS 文本转换为浏览器可以理解的结构——styleSheets
    2. 转换样式表中的属性值,使其标准化 -> 需要将所有值转换为渲染引擎容易理解的、标准化的计算值,这个过程就是属性值标准化
    3. 计算出 DOM 树中每个节点的具体样式
  2. 布局阶段、

    1. 创建布局树
      1. 遍历 DOM 树中的所有可见节点,并把这些节点加到布局树中
      2. 而不可见的节点会被布局树忽略掉

2. 布局计算


#### 前三个阶段: 

1. 浏览器不能直接理解 HTML 数据,所以第一步需要将其转换为浏览器能够理解的 DOM 树结构
2. 生成 DOM 树后,还需要根据 CSS 样式表,来计算出 DOM 树所有节点的样式;
3. 最后计算 DOM 元素的布局信息,使其都保存在布局树中

  1. 分层、

    1. 页面中有很多复杂的效果,如一些复杂的 3D 变换、页面滚动,或者使用 z-indexing 做 z 轴排序等,为了更加方便地实现这些效果,渲染引擎还需要为特定的节点生成专用的图层,并生成一棵对应的图层树(LayerTree)
      1. 通常满足下面两点中任意一点的元素就可以被提升为单独的一个图层
        1. 第一点,拥有层叠上下文属性的元素会被提升为单独的一层
        2. 第二点,需要剪裁(clip)的地方也会被创建为图层
      2. 元素有了层叠上下文的属性或者需要被剪裁,满足其中任意一点,就会被提升成为单独一层。
  2. 绘制、

    1. 渲染引擎实现图层的绘制与之类似,会把一个图层的绘制拆分成很多小的绘制指令,然后再把这些指令按照顺序组成一个待绘制列表
  3. 分块、

    1. 通常一个页面可能很大,但是用户只能看到其中的一部分,我们把用户可以看到的这个部分叫做视口(viewport)
    2. 在有些情况下,有的图层可以很大,比如有的页面你使用滚动条要滚动好久才能滚动到底部,但是通过视口,用户只能看到页面的很小一部分,所以在这种情况下,要绘制出所有图层内容的话,就会产生太大的开销,而且也没有必要
    3. 基于这个原因,合成线程会将图层划分为图块(tile),这些图块的大小通常是 256x256 或者 512x512
  4. 光栅化和合成

    1. 合成线程会按照视口附近的图块来优先生成位图,实际生成位图的操作是由栅格化来执行的。所谓栅格化,是指将图块转换为位图。而图块是栅格化执行的最小单位。渲染进程维护了一个栅格化的线程池,所有的图块栅格化都是在线程池内执行的

一个完整的渲染流程大致可总结

  1. 渲染进程将 HTML 内容转换为能够读懂的 DOM 树结构。
  2. 渲染引擎将 CSS 样式表转化为浏览器可以理解的 styleSheets,计算出 DOM 节点的样式。
  3. 创建布局树,并计算元素的布局信息。
  4. 对布局树进行分层,并生成分层树。
  5. 为每个图层生成绘制列表,并将其提交到合成线程。
  6. 合成线程将图层分成图块,并在光栅化线程池中将图块转换成位图。
  7. 合成线程发送绘制图块命令 DrawQuad 给浏览器进程。
  8. 浏览器进程根据 DrawQuad 消息生成页面,并显示到显示器上。

重排 重绘 合成

  1. 更新了元素的几何属性(重排)

    如果你通过 JavaScript 或者 CSS 修改元素的几何位置属性,例如改变元素的宽度、高度等,那么浏览器会触发重新布局,解析之后的一系列子阶段,这个过程就叫重排。无疑,重排需要更新完整的渲染流水线,所以开销也是最大的

  2. 更新元素的绘制属性(重绘)

    如果修改了元素的背景颜色,那么布局阶段将不会被执行,因为并没有引起几何位置的变换,所以就直接进入了绘制阶段,然后执行之后的一系列子阶段,这个过程就叫重绘。相较于重排操作,重绘省去了布局和分层阶段,所以执行效率会比重排操作要高一些。

  3. 直接合成阶段

    我们使用了 CSS 的 transform 来实现动画效果,这可以避开重排和重绘阶段,直接在非主线程上执行合成动画操作。这样的效率是最高的,因为是在非主线程上合成,并没有占用主线程的资源,另外也避开了布局和绘制两个子阶段,所以相对于重绘和重排,合成能大大提升绘制效率。