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

综述

简单总结

  • 回流性能消耗大,尽量避免回流
  • 不要频繁获取布局信息,对频繁变化元素采用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穿插进行
  • 对动画元素使用定位为absolute或fxed脱离文档流,则其改变时无需回流其他元素
  • 避免使用calc()表达式
  • 避免使用 table 布局,一处改动会造成整个 table 回流
  • 尽可能在 DOM 树的最子级操作dom,引起的回流较小
  • 启用复合图层(CSS3硬件加速、GPU加速)
  • 合并修改或离线后修改(因现代浏览器已采用队列模式,无需手动优化)
    • 把 DOM 离线后修改,如display:none、documentFragment、克隆dom对象等方式
    • 使用cssText或class代替一次性修改多个样式
const el = document.getElementById('test');
el.style.cssText += 'border-left: 1px; border-right: 2px; padding: 5px;';

复合图层(GPU加速)

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

  • 通常有如下方法创建复合图层:
    1. will-change: left, top;(默认值为auto) 传入即将变化的值,让浏览器实现做好准备。
      注意:过度使用会导致更严重的性能问题。
    2. 启用3D效果,如 translate3d、translateZ、rotateZ、backface-visibility:hidden
      一个常见的启用复合图层的手段:translateZ(0)
    3. 涉及opacity/transform的 animation 或者 transition(只有动画执行的过程中才会创建合成层,动画开始前或结束后元素还会回到之前的状态)
    4. <video>、<webgl>、带有复合图层内容的 <iframe>、 3D或硬件加速的2D<canvas> 等元素
    5. 元素有包含复合层的后代节点,且元素本身具有transform、opactiy(小于 1)、mask、fliter、reflection、overflow(不为visible)、position:fixed、
    6. 元素有一个z-index较低且包含一个复合层的前置兄弟元素(换句话说就是该元素在复合层之上渲染)
      元素与复合图层元素发生重叠,则元素也形成复合图层。
      如元素本身存在属性overflow:auto 或 overflow:scroll,则其所有子元素也形成复合图层。
    7. 其它,譬如以前的flash插件
硬件加速时请使用较大的z-index

如果这个元素添加了硬件加速,并且z-index层级比较低 (或为static,即没有z-index层级),那么在这个元素的后面其它元素(z-index不小于该元素且为relative或absolute定位)按照上述创建方式6,会默认变为复合层渲染,如果处理不当会极大的影响性能

  • 通过以下方法解决该问题

    • 将该元素z-index变得比后方其他元素大
    • 将该元素移动到文档最后
    • 给后方元素一个更小的z-index
  • 在这篇文章中的demo可以看出z-index的必要。
    页面中设置了一个h1标题,应用了translate3d动画,使得它被放到composited layer中渲染,然后在这个元素后面创建了2000个list。在h1元素未设置z-index的情况下,本不需要提升到合成层的ul元素下的每个li元素都提升为一个单独合成层(每个li元素的黄色提示边框),最终会导致GPU资源过度消耗页面滑动时很卡,尤其在移动端(安卓)上更加明显。
    选中"为动画元素设置z-index",可以看出ul下的每个li都回归到普通渲染层,不再是合成层也就不会消耗GPU资源去渲染,从而达到了优化页面性能优化的目的。

通过该设置可以查看复合图层
最后编辑于 :
©著作权归作者所有,转载或内容合作请联系作者
【社区内容提示】社区部分内容疑似由AI辅助生成,浏览时请结合常识与多方信息审慎甄别。
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

相关阅读更多精彩内容

友情链接更多精彩内容