重排(回流)和重绘,以及GPU加速那点事

时间:2020-9-7 作者:admin

一、浏览器渲染页面流程

开篇先盗图,以下是李兵老师在讲解《浏览器工作原理与实践》时提供的一张图

重排(回流)和重绘,以及GPU加速那点事

从上图可知,浏览器下载html后,大致的流程是

  1. DOM解析
  2. 样式解析
    a. style标签会先解析
    b. link标签会先下载,再解析
    c. 以上两种情况都会阻塞DOM解析
  3. script下载、执行
    a. 遇到内嵌script代码,会执行该代码,执行之前会等待2步骤完成,因为js可能需要操作样式
    b. 遇到外链script标签,会下载并执行js代码
    c. 以上两种情况都会阻塞DOM解析
    从以上情况可知,平时把script放在body标签最后的原因:提升首屏渲染速度、避免js获取不到DOM元素等

  4. 生成布局树

    重排(回流)和重绘,以及GPU加速那点事

    每个dom节点都对应这一个布局树的节点,从上图的对应关系看,并不是所有的dom节点都对应图层树一个layer,如果没有对应的layer,该节点将隶属于它的父元素的layer。那么,如何才能生成一个layer呢?以下两种情况会产生一个layer:
    • 拥有层叠上下文的元素
    position: fixed,absolute等脱离文档流的属性
    opacity 透明度
    filter 过滤等
    …欢迎补充
    • 需要剪裁(clip)的地方也会被创建为图层,比如出现滚动条

  5. 生成图层树
    生成图层树后,就开始绘制了,也就是图1中paint步骤,其实paint并不是真正的绘制,而是生成一个绘制列表,然后将绘制列表交给合成线程,之后由GPU生成位图,最后由浏览器进程渲染到页面上。也就是说paint之后,主线程就空闲了,如果之后没有任务了。这关系到GPU加速,下面再讲

二、什么是重排(回流)

重排(回流)和重绘,以及GPU加速那点事

从上图是一个浏览器渲染页面的完整流程,但是不是每个流程都是必须执行的,那么重排是什么意思呢?当改变元素宽度,高度等影响布局的属性时,就会触发layout和layer两个流程,这就是所谓的重排或者回流。
重排会占用主线程,因为主线程是单线程的,所以影响性能。比如动画,动画是一帧一帧渲染的,假如每一帧都涉及了重排,并且每一帧之间有一个复杂的js代码执行,就会导致每一帧的渲染时间延长,当渲染时间超过0.16ms时,也就是低于60HZ,就会造成视觉上的卡顿。

三、什么是重绘!

重排(回流)和重绘,以及GPU加速那点事

了解的重排,同理可知,没有layout和layer,直接paint,就是重绘。比如修改background-color, color等不会影响布局的操作。没有了重排,将大大提升性能。
注意:重排一定会造成重绘

四、如何减少重排和重绘

• 使用createElementFragment操作dom,最后一起推入文档
• 使用class去修改样式
• 避免使用table布局
• dom读写分离
• 欢迎补充

五、GPU加速

了解了重排和重绘,那么GPU加速又是什么呢,其实就是GPU为该元素单独绘制一个复合层,当修改样式时不影响其他复合层。以下样式将开启GPU加速:<br/>

• transform 动画
• opacity 动画
• 设置translateZ()、translate3D()
• 设置will-change等
下面我们看下浏览器GPU加速具体是怎么表现的,先上一段代码

<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Document</title>
  <style>
    @keyframes move {
      from {
        transform: translateX(0);
      }
      to {
        transform: translateX(200px);
      }
    }
    .move {
      animation: move 3s linear;
    }
  </style>
</head>
<body>
  <div class="move" style="background-color: red; width: 100px; height: 100px; position: absolute; z-index: 2;"></div>
  <div style="background-color: blue; width: 100px; height: 100px; position: absolute; z-index: 1; left: 40px;"></div>
</body>
</html>

我们用Chrome浏览器测试一下,打开开发者工具=>layers
首先,页面的布局不是2d的,它其实是一个3D的层叠,假设屏幕横向为X轴,纵向为Y轴,那么我们眼睛看的方向就是Z轴。开启GPU加速的元素,GPU将会为它绘制一个复合层,如下

重排(回流)和重绘,以及GPU加速那点事

在以上动画执行过程中,我们可以看出红色块是单独的复合层,复合层之间是独立的,重排、重绘都不会相互影响,这样就达到了性能优化的目的。
另外,我们可以发现在动画执行之前和之后,红色块是没有单独的复合层,这样页面会闪一下,原因是动画开始前为红色块生成复合层时是经历了一次重绘,同样结束后也经历了一次重绘,那么如何避免呢?
就是上面提到的transform(translateZ、translate3D)、will-change这几个属性,一开始就会给DOM生成一个复合层,这样就不会经历动画前后的重绘。

声明:本文内容由互联网用户自发贡献自行上传,本网站不拥有所有权,未作人工编辑处理,也不承担相关法律责任。如果您发现有涉嫌版权的内容,欢迎进行举报,并提供相关证据,工作人员会在5个工作日内联系你,一经查实,本站将立刻删除涉嫌侵权内容。