前端性能优化 - 减少重绘与回流

在页面加载时,浏览器把获取到的HTML代码解析成1个DOM树,DOM树里包含了所有HTML标签,包括display:none隐藏,还有用JS动态添加的元素等。
  浏览器把所有样式(用户定义的CSS和用户代理)解析成样式结构体
  DOM Tree 和样式结构体组合后构建render tree, render tree类似于DOM tree,但区别很大,因为render tree能识别样式,render tree中每个NODE都有自己的style,而且render tree不包含隐藏的节点(比如display:none的节点,还有head节点),因为这些节点不会用于呈现,而且不会影响呈现的,所以就不会包含到 render tree中。我自己简单的理解就是DOM Tree和我们写的CSS结合在一起之后,渲染出了render tree。

回流

当render tree中的一部分或全部因为元素的规模尺寸、布局,隐藏等改变而需要重新构建,就称为回流。页面布局和几何属性改变时就需要回流。

每个页面至少需要一次回流,就是在页面第一次加载的时候,这时候是一定会发生回流的,因为要构建render tree。在回流的时候,浏览器会使渲染树中受到影响的部分失效,并重新构造这部分渲染树,完成回流后,浏览器会重新绘制受影响的部分到屏幕中,该过程成为重绘。

重绘

当render tree中的一些元素需要更新属性,而这些属性只是影响元素的外观,风格,不会影响布局,比如background-color

回流和重绘的关系

回流必将引起重绘,而重绘不一定会引起回流。

避免重绘和回流的两种方式

1. 回流

  • 盒模型相关的属性会触发重新布局,会触发页面回流

width height padding margin display border-width border min-height min-weight

  • 定位属性及浮动也会触发重新布局

top right bottom left position float clear

  • 改变节点内部文字结构也会触发中心布局

Text-aligin over-flow-y font-wieght overflow font-family line-height vertical-align white-space font-size

2. 重绘

color border-style border-radius visivbility text-tecoration background background-image background-position background-repeat background-size outline-color ouline outline-style outline-width box-shadow

 


例如:
        可以看到爱奇艺的视频,在layers中可以看到本页面的所有图层,还可以看到图层的信息,比如为什么要单独作为一个图层:Compositing Reasons


在重绘的时候,paint flashing 会将重绘的元素标绿,比如video在播放的时候一直在重绘

新建DOM的过程

  1. 获取dom后分割为多个图层
  2. 对每个图层的节点计算样式结果(Recalculate style -- 样式重新计算)
  3. 为每个节点生成图形和位置(Layout -- 回流和重布局)
  4. 将每个节点绘制填充到图层位图中(Paint Steup 和 Paing -- 重绘)
  5. 图层作为纹理上传至GPU
  6. 组合多个图层到页面上生成最终屏幕图像(Composite Layers -- 图层重组)

将频繁重绘回流的DOM元素单独作为一个独立图层,那么这个DOM元素的重绘和回流的影响只会在这个图层中。

如何将dom元素变成新的独立图层

Chrome 创建图层的条件

  1. 3D或透视变换css属性(perspective transform)
  2. 使用加速视频解码的<video>节点
  3. 拥有3D(WebGL)上下文或加速的2D上下文的Canvas节点
  4. 混合插件如flash
  5. 对自己的opacity做css动画或使用一个动画webkit变换的元素
  6. 拥有加速css过滤器的元素
  7. 元素有一个包含复合层的后代节点(一个元素拥有一个子元素,该子元素在自己的层里)
  8. 元素有个一z-index较低且包含一个符合层的兄弟元素(换句话说就是该元素在复合层上面渲染)

优化点

1. 浏览器的优化机制

现代的浏览器都是很聪明的,由于每次重排都会造成额外的计算消耗,因此大多数浏览器都会通过队列化修改并批量执行来优化重排过程。浏览器会将修改操作放入到队列里,直到过了一段时间或者操作达到了一个阈值,才清空队列。但是!当你获取布局信息的操作的时候,会强制队列刷新,比如当你访问以下属性或者使用以下方法:

offsetTop、offsetLeft、offsetWidth、offsetHeight、scrollTop、scrollLeft、scrollWidth、scrollHeight、clientTop、clientLeft、clientWidth、clientHeight、getComputedStyle()getBoundingClientRect

以上属性和方法都需要返回最新的布局信息,因此浏览器不得不清空队列,触发回流重绘来返回正确的值。因此,我们在修改样式的时候,最好避免使用上面列出的属性,他们都会刷新渲染队列。

如果我们要经常去获取和操作这些值,则可以先将这些值缓存起来。

2. 优化

因为获取属性值会触发回流,因为回流有缓存机制,在使用属性值读取时,会强制刷新缓冲区,因为要获取真实的数据,会导致缓冲机制失效

图层不能被滥用,因为会影响性能,在图层合成上会消耗大量的时间

  1. 不要通过父级来改变子元素样式,最好直接改变子元素样式,改变子元素样式尽可能不要影响父元素和兄弟元素的大小和尺寸。
  2. 尽量通过class来设计元素样式,切忌用style。
  3. 对于复杂动画效果,使用绝对定位让其脱离文档流,否则会引起父元素及后续元素大量的回流。
  4. 不要用table布局的另一个原因就是tables中某个元素一旦触发reflow就会导致table里所有的其它元素reflow。在适合用table的场合,可以设置table-layout为auto或fixed。
  5. 避免设置多层内联样式。
  6. 使用修改样式cssText.
  7. 避免使用触发重绘,回流的css属性
  8. 将重绘、回流的影响范围限制在单独的图层之内
  9. 使用translate替代top
  10. 使用opacity 替换visibility
  11. 不要一条一条的修改dom样式,预先定义好class,然后修改DOM的className
  12. 把dom离线修改,比如,先把DOM给display: none(有一次Reflow),然后你修改100次,然后再把它显示出来
  13. 不要把DOM节点的属性值放在一个循环里当成循环里的变量,例如offsetHeight, offsetWidth
  14. 动画实现速度的选择,限制频率,因为消耗cpu资源
  15. 对于动画新建图层 video canvas transform属性
  16. 启用GPU加速,在使用位置变化时比如webGL

为什么不用top

因为top会触发回流,但是translate不会,举一个例子

示例

1. 使用top

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <style>
        .text-box{
            position: absolute;
            left: 0;
            top: 0;
            width: 100px;
            height: 100px;
            background: red;
        }
    </style>
</head>
<body>
<div class="text-box">

</div>
<script>
    window.onload = () => {
        setTimeout(() => {
            let boxDomList = document.querySelectorAll('.text-box');
            boxDomList[0].style.top = '100px';
        }, 2000)
    }
</script>
</body>
</html>

可以看到在2s的时候,top会变成100px

第一阶段: Reaclculate Style                         68μs

第二阶段: Layout               触发回流过程      25μs

第三阶段: Update Layer Tree                       68μs

第四阶段: Paint              重绘                23μs

第五阶段: Composite Layers    图像合成            0.11ms

共用 68 + 25 + 68 + 23 + 110 = 294μs

2. 改用translate

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <style>
        .text-box{
            /*position: absolute;*/
            /*left: 0;*/
            /*top: 0;*/
            transform: translateY(0);
            width: 100px;
            height: 100px;
            background: red;
        }
    </style>
</head>
<body>
<div class="text-box">

</div>
<script>
    window.onload = () => {
        setTimeout(() => {
            let boxDomList = document.querySelectorAll('.text-box');
            boxDomList[0].style.transform = 'translateY(100px)';
        }, 2000)
    }
</script>
</body>
</html>

没有layout回流

第一阶段: Reaclculate Style                         80μs

第二阶段: Update Layer Tree                       60μs

第三阶段: Composite Layers    图像合成            75μs

总时间:80 + 60 + 75 = 215μs

相较于第一次节省了79μs,虽然不多,但是如果布局很复杂,回流很多,页面会得到很明显的收益

使用场景:页面浮窗

 

发布了5 篇原创文章 · 获赞 0 · 访问量 118

猜你喜欢

转载自blog.csdn.net/forteenBrother/article/details/105616725