5

浏览器渲染流程(下)

 2 years ago
source link: https://www.clzczh.top/2022/08/11/%E6%B5%8F%E8%A7%88%E5%99%A8%E6%B8%B2%E6%9F%93%E6%B5%81%E7%A8%8B2/
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

浏览器渲染流程(下)

上一篇讲了一点非常普遍的部分,可能很常听别人说(虽然重绘部分还没讲)。

这一篇会讲点相对来说较少听到过的,如分层、光栅化、合成。

4. 分层(Layer)

因为页面中有很多复杂的效果,像是3D变换,页面滚动等,为了更方便的实现这些效果,渲染引擎回味特定的节点生成专用的图层,并生成一颗对应的图层树,最后再合成图层。

怎么查看页面的分层情况呢?

202207311329432.png

按上图操作即可。

202207311331905.png

每一个黑框都是一个图层。

那么需要满足什么条件,渲染引擎才会为特定的节点创建新的图层呢?

  1. 拥有层叠上下文属性的元素会被提升为单独的一层

    <style>
        .bottom {
          position: fixed;
          width: 100px;
          height: 100px;
          background-color: pink;
        }
    
        .top {
          position: absolute;
          width: 50px;
          height: 50px;
          background-color: purple;
          z-index: 1;
        }
    </style>
    
    <body>
      <span class="bottom"></span>
      <span class="top"></span>
    </body>
    
    202207311341874.png
  2. 需要裁剪的地方也会被创建为图层

    这里的剪裁就是,当内容超过容器体积时,对文字进行裁剪。$\color{red}overflow: hidden不会创建新的图层$。

    <style>
        .clip {
          width: 100px;
          height: 100px;
          background-color: pink;
          overflow: auto;
        }
    </style>
    
    <body>
      <div class="clip">
        <p>123456789123</p>
        <p>123456789123</p>
        <p>123456789123</p>
        <p>123456789123</p>
      </div>
    </body>
    
    202207311400189.png

出现要裁剪的时候,渲染引擎回为文字部分当都创建一个图层。滚动条也会是一个图层。(包括上下、左右两条滚动条)

5. 绘制(Paint)

分层结束后,我们会得到图层树,然后渲染引擎就会对图层树上的每个图层进行绘制。

而绘制的过程就是模仿画画,会把涂层的绘制拆分成很多个绘画指令。我们想要绘制只需要依次执行一个绘制列表的每一条指令即可,比如,画一个矩形,画一个边框等。

那么怎么查看绘制的指令呢?

打开Layer面板,按下图步骤操作。

202207311752485.png
202207311801662.png

可以发现,绘制指令还会包括两部分:

  • rect:绘制的范围。如果是其他形状可能不是rect,而是rrect之类。

  • paint:绘制的一些样式,包括是填充还是线这种

6.1 光栅化(Raster)

上一步(绘制)中,我们看到了绘制指令列表。但是实际的绘制操作并不是主线程来完成的,而是合成线程来完成的。

渲染进程中主线程和合成线程的关系如下图所示:

202207311858189.png

当图层的绘制指令列表准备好之后,主线程会把该列表提交(commit)给合成线程。然后合成线程开始工作:

  1. 合成线程将图层划分为图块(tile)

  2. 图块栅格化

**合成线程将图层划分为图块(tile)**:

通常一个页面会很大(长),但是用户只能看到其中一部分,而这一部分叫做视口(viewport)。有一些图层也会很大,但是用户只能通过视口看到一部分,所以就没必要将整个图层都绘制出来。这就是将图层划分成图块的原因。

202207311925446.png

图块栅格化:将图块转换为位图。(会优先将视口附近的图块先转换为位图)

渲染进程维护了一个栅格化的线程池,所有的图块栅格化都是在线程池内执行的。而且栅格化过程中会使用GPU来加速生成位图,使用GPU生成位图的过程叫做快速栅格化,生成的位图会保存在GPU内存中。

0097d6ae17034847926c20b3e1cbfcc2~tplv-ho98pwquqr-image.jpg?x-expires=1966479013&x-signature=BDvmdJTrK7EOeIytZncLlarWsxo=

6.2 合成(Composite)与显示

当所有的图块都被光栅化后,合成线程就会生成一个绘制图块的命令(DrawQuad),然后将该命令提交给浏览器进程。浏览器进程中的组件viz会根据该命令,将页面内容绘制到内存中,最后将页面内容从内存中拿出来,显示在屏幕上。

合成操作是在合成线程上完成的,也就是说,执行合成操作时,是不会影响到主线程的。

7. 完整流程

202207311734387.png

回流、重绘、合成

回流(Reflow)

回流需要重新根据CSSOM和DOM来计算布局树,然后完整执行渲染流水线,包括分层、绘制、合成(光栅化)。

回流也叫重排

202208011043141.png

从上图也可以看出来,回流需要完整执行渲染流水线,所以开销也是最大的。

会导致回流的操作(以及减少回流的方法)

  1. DOM的增删行为:如果需要大量增删子元素,最好使用DocumentFragment文档碎片来减少回流

  2. 几何属性的变化:如果需要修改多个属性,例如同时修改宽高、字体大小等,可以将这些定义在一个class里,这样就只会引起一次回流。

  3. 元素位移操作:使用transform代替top等位移操作。因为transform会开启硬件加速(GPU加速),后面合成小节会讲到。

  4. 获取元素的偏移量属性:$\color{red}获取$offsetTopscrollTopclientTopoffsetWidth等属性,因为浏览器为了确保值得正确性,所以即使只是获取属性值也会引起回流。少获取元素偏移量属性。如果要获取偏移量属性而且是多次操作,最好做下缓存。

  5. 浏览器窗口尺寸改变

  6. 初始渲染

重绘(Repaint)

如果修改元素的背景颜色,不会触发布局、分层阶段,直接进入绘制阶段,然后执行之后的子阶段,这个过程就叫重绘。

202208011057128.png

重绘不会触发布局、分层阶段,所以效率比起回流要高很多。

如果使用CSS的transform来实现动画效果,会跳过布局和绘制阶段,直接在非主线程进行合成动画。合成的效率比回流、重绘要高很多,因为合成是在非主线程进行合成,还跳过了布局和绘制阶段。

202208011117745.png

可以在CSS Triggers查看,那些属性会触发回流、重绘、合成。有一些属性还会因为内核不同,触发的也不同。

技术淘金丨浏览器渲染流程

浏览器原理4:页面渲染 - 简书


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK