从图层的角度去看关键渲染路径的布局到合成

·  阅读 480

DOM树解析阶段

将html解析成dom树:主要过程分为解码,令牌化,生成dom节点,根据dom节点关系构建dom树。dom树的构建是在网页内容下载过程中创建的,即获取到html字节,解析字节到构建dom树的过程中同时还在继续下载其他内容比如图片,样式,脚本等

  • 1,解码:将字节数据根据其编码方式转换成对应字符
  • 2:令牌化(token):根据W3C规范将字符转换成令牌(token),令牌匹配都有自己规则,比如<p class="a">text text text</p>,当我们匹配到<我们并不知道它是哪一个token,继续匹配下一个字符<p,这时我们知道这时这个是p标签的开始的token,按照这种方式根据不同的token规则将字符串全部转化成token,如图
  • 3:根据令牌生成对应dom节点
  • 4:根据dom节点关系生成dom树

CSSOM树解析阶段

CSS解析成CSSOM树(CSSOM:CSS object model):同DOM树解析过程类似,解码,令牌化,生成CSS节点,生成CSSOM树。css解析与dom解析是并行的

布局阶段

布局阶段既计算出DOM树中可见元素的几何位置

  • 1,创建布局树:从根节点开始遍历dom树中的可见节点(display:node与head标签内元素为不可见节点,在此阶段会过滤掉),将可见节点添加到布局树中

  • 2,布局计算:将节点与对应的样式对象合并,计算出展示节点具体样式

分层阶段

  • 布局树中拥有层叠上下文的属性会单独提升成渲染图层(比如dom根元素,opacity属性,z-index,绝对定位等都会形成渲染图层),同时,对于内容溢出及需要裁剪元素导致出现滚动条也需要单独提升为渲染图层

绘制阶段

  • 1,创建绘制列表:将绘图指令按照顺序创建绘制列表,如下既绘制指令列表: image.png
  • 2,栅格化(或者叫它光栅化):栅格化既将当前图层划分为图块(所以绘制是以图层为单位),然后将图块生成对应的位图(位于视口附近的图块优先去生成位图) image.png

合成&&显示阶段

  • GPU将上传过来的位图进行合成,合成完毕之后,将发出一个绘制图块的命令DrawQuad

  • 浏览器接受到该命令将从GPU内存中读取合成后的图像,最终展示出来

    • 具体点来说:浏览器将从GPU内存中读取合成后的图像输出到显卡后缓冲区,显卡后缓冲区将内容交给前缓冲区,最终由屏幕以60HZ的频率刷新显示图像

从图层再开始理解绘制阶段与合成阶段

1,渲染图层:拥有层叠上下文属性的元素既渲染图层(或者叫它层叠上下文对象),这其中分为三种图层,既普通渲染图层,根合成图层以及合成图层。对于一般渲染图层而言,只要它没有被提升至合成图层,那么它永远属于根合成图层一部分

  • 渲染图层可以理解成浏览器Z轴上的一个个平面,比如绝对定位,z-index都会生成渲染图层,想想,是不是这些行为都会让你的页面分层

  • 层叠上下文属性(或者说哪些属性可以形成渲染图层):

    • 1,文档根元素 ,称为“根层叠上下文”。

    • 2,position属性的值不为static且z-index值不为auto或0

    • 3,opacity属性值小于1的元素

    • 4,flex布局的元素

    • 5,will-change值设定任意属性且值为非初始化值

    • 6,transform值不为none

    • 7,filter值不为none

      image.png

  • 三类渲染图层

    • 根合成图层:文档根元素称为根层叠上下文,既根合成图层

    • 合成图层:满足形成合成图层条件的元素,会被提升为复合图层

    • 普通渲染图层:不满足复合图层条件切不是文档根元素的图层,都是普通渲染图层

2,根合成图层(也可以叫它根层叠上下文对象或者根复合图层):文档根元素既根合成图层,该图层内所有元素都共用同一个图形上下文

  • 该图层内所有元素指的是:只要不是合成图层,不管该元素是否脱离文档流,不管该元素是否形成渲染图层,还是说它就是一个普通DOM元素,它都属于根合成图层

  • 图形上下文:图形上下文服务于当前图层(根合成图层或合成图层)的内存管理(既在RAM记录图层信息,其实就是当前图层的位图)以及输出当前图层的位图(由CPU进行计算当前图层的绘制信息得到位图,再将该位图输出到RAM中)

  • 什么是位图:构成当前图块的像素信息

3,合成图层(也可以叫他复合图层):一些满足形成合成图层条件的元素,会被提升成合成图层(同时也会脱离文档流)。合成图层拥有属于自己的图形上下文,合成图层的绘制依然是CPU在做,CPU输出当前合成图层的位图到RAM中,之后所有位于RAM中的合成图层的位图会被上传到GPU中(准确来说是GPU的VRAM中),GPU对这些位图进行合成(所以GPU做的事是将合成图层的位图进行合成,并不包括位图的绘制行为)

  • 合成图层与渲染图层之间的关系:合成图层可以看做拥有自己的绘制资源(图形上下文)的特殊渲染图层,而普通的渲染图层它们没有自己的绘制资源(普通渲染图层共用根合成图层的图形上下文)

4,合成图层作用(或者说为什么开启硬件加速会流畅):

合成图层独立于根合成图层,它拥有自己的绘制资源(图形上下文)且不与其他图层(根合成图层或其他的合成图层)有干涉,因此合成图层中发生的变化(回流,重绘)不会影响到其他图层(比如根合成图层或者其他合成图层),所以其他图层不需要回流重绘,从而避免了整个页面的回流重绘

且当前合成图层仅仅是使用自己的图形上下文重新进行绘制,输出位图,然后上传给GPU去完成图层合成,同时合成图层的合成是GPU完成(而GPU处理图像本来就比CPU快),最后展示在屏幕中

而且正是由于合成图层有自己独立绘制资源,因此合成图层绘制(或者说是输出位图,虽然此部分是使用CPU与RAM)的速度也很快

  • 什么是纹理:既从RAM中上传到GPU的位图(还是位图,只不过在GPU中它叫纹理)

  • 创建合成图层的条件:

    • 1,3D变换:比如 translate3d,translateZ依此类推(注意不是2D转换,3D转换需要包括Z)

    • 2,使用了属性opacity或者transform的 CSS animation 的元素

    • 3,有合成层后代同时本身是 fixed 定位

    • 4,设置了will-change属性,比如 will-change:auto

    • 5,使用加速视频解码的

    • 6,与有一个合成层并且带有很低 z-index的元素是兄弟元素的元素。(也就是说在合成层上面渲染的元素)

    • 7,与有一个合成层并且带有很低 z-index的元素是兄弟元素的元素。(也就是说在合成层上面渲染的元素)

    • ......

    • 注意:当元素没有或隐藏的时候不会创建图层(布局阶段那一步就把这些元素过滤掉了)

5,合成图层中不需要回流重绘场景:如果一个合成图层,仅仅是它的合成属性opacity或transform发生变化,该层则不需要重绘,重新对该图层进行合成即可(看,这里避免了回流与重绘,虽然没有避免合成这一步)

  • 为什么transform动画没有触发回流重绘:简而言之,transform动画由GPU控制,支持硬件加载,并不需要软件方面支持渲染,GPU只需要重新对已存在与GPU中的该复合图层的纹理(位图)重新合成即可

6,层爆炸与层压缩

  • 层爆炸:见生成合成层条件7,如果一个z-index很小的元素是合成图层,那么如果它有大量兄弟节点,且兄弟节点的z-index大于该合成图层,那么就会隐式生成大量的非必要合成图层,既层爆炸,从而占用大量内存(RAM)与绘制资源(CPU,GPU)

  • 层压缩:上面对层爆炸的举例可以看到,我们可以很容易的意外的创建大量合成图层,从而消耗CPU与内存(RAM)资源,因此,浏览器页提供了应对机制,既层压缩,具体就是,如果多个渲染图层同一个合成图层发生重叠时,这些渲染图层会被压缩到一个图形层(合成图层中)中,防止出现层爆炸,所以对于上述层爆炸举例中那个因为兄弟节点是合成图层且zindex较小,导致其他zindex较大的兄弟节点隐式生成大量合成图层的场景,层压缩就可以将这些隐式生成的所有重叠合成图层压缩到一个合成图层中,从而节省资源。

    • 但是层压缩并非万能,比如对于video元素的图层就无法被压缩,同时也无法将别的渲染图层压缩到video元素所在的合成图层中,再比如如果会打破渲染顺序,那么层压缩也无法生效,除此之外还有许多其他场景,因此不要太过于依靠层压缩机制,还是得合理控制自己的代码规范。

7,提升合成层最好的方式:

  • 设置will-change属性,可以设置will-change属性为opacity,transform,top,left,right,bottom将当前元素提升成合成图层。注意,对于top,left等属性的will-change的设置需要设置其对应的定位属性(relative等)

  • 对于不兼容will-change属性的浏览器,常用提升合成层的方法是设置3D transform属性将其强制提升为合成图层,如下CSS设置:

    #target{
        transform:translateZ(0);
    }
    复制代码

8,合成图层相关的性能优化

  • 1,使用transform或opacity来实现动画效果:因为最理想的渲染流水线是不需要回流与重绘,只需要做合成环节即可,因此满足该条件的合成层属性只有transform与opacity

    • 注意:只有当元素提升为合成图层时,transform 和 opacity 才不会触发 paint,如果不是合成层,依然会触发paint
  • 2,减少绘制区域:对于页面中一些固定不变的区域,提升为合成图层,避免页面重绘时连带一起重新绘制

    • 举个例子:比如fix固定在顶部的导航栏,在页面发生重绘时,该导航栏也会被重新绘制,而导航栏一般是不变的,因此可以将其提升成合成图层,避免导航栏区域重绘**
  • 3,避免滥用合成图层:合成图层带来的性能优化时显著的,但是其代价是CPU与内存的开销,因此避免滥用合成图层,以及一些隐式合成图层的生成(层爆炸)等

感谢参考:

分类:
前端
标签:
分类:
前端
标签:
收藏成功!
已添加到「」, 点击更改