回流重绘、复合图层、硬件加速

综述

简单总结

  • 回流性能消耗大,尽量避免回流
  • 不要频繁获取布局信息,对频繁变化元素采用* absolute/fixed/硬件加速(opacity、transform动画、3D属性)
  • 硬件加速时要注意复合层会将其父元素和前置高Index兄弟元素都变成复合层,过多复合层也会影响性能
  • 使用transform代替left、top减少使用引起页面重排的属性

回流和重绘

  • 回流(重排:Reflow 对应Performance的Rendering)
    当页面中的html结构发生改变(增加、删除、移动节点),浏览器都需要重新计算新的DOM结构,重新对当前的页面进行渲染,回流一定会触发重绘。


    回流
  • 重绘 ( Repaint 对应Performance的Painting)
    当某个元素的部分样式(如背景颜色)发生改变但不影响它周围或内部布局的属性时,浏览器需要重新渲染当前元素即可。

    • display:none 会触发 reflow,而 visibility:hidden 只会触发 repaint,因为没有发生位置变化。
    • requestAnimationFrame传入的回调方法会在每次重绘之前被执行
      重绘
  • 合成
    CPU完成回流/重绘后会将图层交给GPU进行合成。
    如果更改一个既不要布局也不要绘制的属性(如transform),则直接由GPU进行合成运算,性能是最好的。

    • 因此CSStransform动画比JS操纵width/height/top/left等性能更好
    • 合成流程不在主线程执行,因此不会和JS或其他布局任务相互阻塞。


      合成

通常有如下行为可以触发回流:
  • 页面初次渲染
  • 添加或删除可见的DOM元素
  • 元素的位置或尺寸发生变化(包括外边距、内边框、边框大小、高度和宽度等)
  • 激活 CSS 伪类(例如:hover)
  • 内容发生变化,比如文本/字体变化或图片被另一个不同尺寸的图片所替代。
  • 浏览器的窗口尺寸变化(因为回流是根据视口的大小来计算元素的位置和大小的)
  • 获取布局信息
    浏览器正常情况下会将dom的修改加入一个队列,等达到阈值或经过一段时间一起回流。但当JS试图获取布局信息时会强制进行一次layout,以保证获取的信息是dom修改之后的。因此以下属性应避免频繁调用,且不要和修改dom穿插进行:
    • offsetTop、offsetLeft、offsetWidth、offsetHeight
    • scrollTop、scrollLeft、scrollWidth、scrollHeight
    • clientTop、clientLeft、clientWidth、clientHeight
    • getComputedStyle()
    • getBoundingClientRect
    • scrollTo()
避免回流的方法
  • 不要频繁调用布局信息,且不要和修改dom穿插进行
  • 对动画元素使用定位为absolutefxed脱离文档流,则其改变时其他元素只需重绘无需回流
  • 避免使用calc()表达式
  • 避免使用 table 布局,一处改动会造成整个 table 回流
  • 尽可能在 DOM 树的最子级操作dom,引起的回流较小
  • 启用复合图层(CSS3硬件加速、GPU加速)
  • 合并修改或离线后修改(因现代浏览器已采用队列模式,无需手动优化)
    • 把 DOM 离线后修改,如display:nonedocumentFragment、克隆dom对象等方式
    • 使用cssTextclass代替一次性修改多个样式
const el = document.getElementById('test');
el.style.cssText += 'border-left: 1px; border-right: 2px; padding: 5px;';

复合图层

复合图层会触发硬件加速,通过浏览器的GPU进程使用GPU而非CPU进行渲染
默认情况下所有dom处于同一图层(absolutefixed也在默认图层)
复合图层repaint时,只repaint本身,不会影响到其他的层
复合图层transformopacity变化,只触发合成,不会触发回流和重绘
复合图层会占用额外资源,因此复合图层过多也会影响性能。
通过Chrome开发者工具=>more tools=>layers可以查看图层

  • 通常有如下方法创建复合图层:
    1. will-change 这是最合理的方式,但兼容性较差
    2. 启用3D效果,如 translate3dtranslateZrotateZ
    3. 涉及opacity/transform的动画(只有动画执行的过程中才会创建合成层,动画开始前或结束后元素还会回到之前的状态)