你居然不知道回流和重绘?

212 阅读4分钟

很重要的前端基础知识。面试的时候没回答上来,为此直接寄寄了。

说回正事:回流和重绘是前端很重要的基础知识,和性能优化息息相关。

  1. 什么是回流?

回流也可以叫重排:可以理解为对元素重新排列?

我们都知道浏览器根据我们写的代码计算过样式之后,将计算结果渲染到页面上,每一次重新渲染都会造成回流(重新排列,重新组合页面)。

常见的重排原因:

  1. 添加或者删除DOM: append remove
  2. 改变元素的尺寸 : 改变盒子模型
  3. 改变元素内容:比如内容content变大,把盒子撑开?
  4. 页面渲染初始化:首次加载页面
  5. 窗口尺寸变化: 重新计算内容大小、位置。
  6. 计算布局信息:Js查询某些布局的信息,如offsetWidthoffsetHeightscrollTopscrollLeft等时,浏览器需要重新计算布局以返回最新的值,这是因为这些属性依赖于当前的布局信息。

全局范围

全局范围的重排对性能的消耗很大,比如首次加载页面。渲染主线程要重新计算渲染DOM树。

局部范围

局部范围的重排相对影响较小。


  1. 什么是重绘?

当我们修改DOM样式,不改变DOM元素的几何属性,浏览器不需要重新计算元素的几何属性,直接为该元素绘制新的样式。这个过程叫做重绘(几何不变,重新绘画)。

常见重绘的原因:

  1. 改变外观属性:比如color、bgc、border-color
  2. 透明的变化: opacity
  3. css滤镜:filter
  4. 动画和过渡
  5. 视频播放

  1. 回流与重绘的关系

  1. 回流必然重绘,重绘不一定回流。
  2. 回流性能消耗比重绘大。

  1. 如何减少回流与重绘(优化)

  1. js修改样式时,尽量使用动态className(样式集中操作)

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <style>
        .box {
            width: 200px;
            height: 200px;
            background-color: red;
            transition: all 0.5s;
        }

        .in {
            width: 100px;
            height: 100px;
            background-color: blue;
        }
    </style>
</head>

<body>
    <div class="box"></div>
    <script>
        const box = document.querySelector('.box')
        box.addEventListener('mouseenter', () => {
            box.style.width = '100px'
            box.style.height = '100px'
            box.style.backgroundColor = 'blue'
        })
    </script>
</body>

</html>

上面这种方法,在每一次用js修改样式的时候都会造成回流(宽高)和重绘(颜色)。共计:两次回流,一次重绘。

如果改成动态添加类名就可以减少回流重绘次数,变成一次。

<script>
        const box = document.querySelector('.box')
        box.addEventListener('mouseenter', () => {
            /* box.style.width = '100px'
box.style.height = '100px'
box.style.backgroundColor = 'blue' */
            box.classList.add('in')
        })
</script>
  1. 优化动画

比如,一个动画效果:向右移动100px

  • 如果每次移动1px,换平滑,看上去很丝滑,但是就要移动100次,这就造成了100次的动画效果,及其耗费GPU资源。
  • 如果每次移动3px,假设肉眼看上去一样丝滑(对计算机来说都是以.....ms计算的),这样,同样的效果只计算了33次,降低3倍资源消耗。
  1. 使用GPU加速

如果实在减少不了重排和重绘,就只能调用GPU加速,术业有专攻。

  1. 硬件加速:通过CSS的transformopacityfilter等属性,可以创建一个新的复合层(Compositor Layer),这些层的动画会由GPU处理,从而减少主线程的工作量。例如,使用transform: translate3d()代替topleft来移动元素。
  2. will-change属性:提前告知浏览器某些元素将会发生变化,这样浏览器可以提前做好优化准备,创建一个新的复合层。例如,will-change: transform;
  3. 使用CSS动画:CSS动画通常比JavaScript动画更高效,因为它们可以在浏览器的复合线程上运行,而不占用主线程。
  4. 避免布局颠簸(Layout Thrashing) :在连续的DOM操作中,尽量减少对布局信息的读取和写入,以免引发多次不必要的回流。
  5. 使用requestAnimationFrame:将DOM操作和样式更新放在requestAnimationFrame回调中,这样可以确保这些操作在下一帧动画开始前执行,减少不必要的回流和重绘。
  6. 利用Flexbox和Grid布局:这些现代布局模型比传统的盒模型更高效,可以减少回流和重绘的次数。
  7. 减少复杂度:简化CSS选择器,避免使用复杂的阴影和渐变,减少重绘区域的面积。
  8. 管理图层:通过合理地管理图层,将不经常变化的元素放在单独的图层上,可以减少整个页面的重绘次数。
  9. 使用Web Workers:对于一些复杂的计算和数据处理,可以使用Web Workers在后台线程中执行,避免阻塞主线程,减少回流和重绘。
  10. 监控性能:使用开发者工具的性能监控面板来分析页面性能,找出导致回流和重绘的原因,并针对性地进行优化。