立诚勿怠,格物致知
It's all about connecting the dots

CSS性能:重排、重绘、重组

一、重排和重绘

当HTML树和CSS样式规则拼合成渲染树时,这颗渲染树上的节点已经包含了样式规则(比如宽度、高度、是行内元素还是块级元素等),于是就能计算出他们在页面上的位置,将这些元素根据其位置排列到页面中的过程称之为“排布”。当元素排布好之后,浏览器会接着绘制他们的样子(比如颜色),这个过程叫做“绘制”。

  • 重排”(reflow/relayout)就是重新排布的意思(比如当你修改一个元素的宽度之后,在其后面的元素的位置也会被影响到,这时候就会发生“重排”)。
  • 相似地,“重绘”(repaint)就是重新绘制的意思(比如当你修改了一个元素的背景色,就会发生重绘)。
  • 发生重绘时不一定会发生重排,但是发生重排时一般会发生重绘,因为重新排布后需要将元素绘制到页面上。
  • 重组(recomposite):多个图层的间的重新组合。

什么时候会发生重排:

  • 浏览器窗口大小、缩放比例发生变化时。
  • 增加、移除元素。
  • 修改元素的width、height、padding、margin等。
  • 修改元素内的文本导致宽高度发生变化。
  • 读取元素的width、height等:浏览器会重新计算一遍以保证所读取的值是正确的,所以当我们读取一次值后若清楚这个值不会改变就应该直接把对应的值存起来不要每次都重新读取

什么时候会发生重绘:

  • 修改元素的color、background、outline等。

二、重排和重绘对性能的影响

重排和重绘的影响范围是整个图层。如果一个图层里的东西比较多,当其中一个元素频繁的发生比如尺寸/位置的移动的话,对性能的影响是比较大的。

针对部分频繁操作的行为,chrome浏览器会自动开启图层,主要有以下几种情况:

  1. CSS 3D变化的图形,比如transform: translateX(0)。
  2. HTML5中的<video>标签。
  3. Canvas绘图中的节点。
  4. CSS 动画的节点:keyframes animation。
  5. 拥有CSS加速属性:will-change: transform。

我们自己平时写CSS动画时有几个注意点:

  • 利用transform: translate3d(x, y, z);可借助3D变形开启GPU加速(这会消耗更多内存与功耗,确有性能问题时再考虑);
  • 若动画开始时有闪烁,可尝试:backface-visibility: hidden; perspective: 1000;
  • 尽可能少用box-shadows和gradients这两页面性能杀手;
  • CSS动画属性可能会触发整个页面的重排(reflow/relayout)、重绘(repaint)和重组(recomposite)。其中paint通常是最花费性能的,尽可能避免使用触发paint的CSS动画属性。所以,对元素进行频繁移动时,要尽可能通过修改translate代替修改top/left/bottom/right来实现动画效果,可以减少页面重绘(repaint),前者只触发页面的重组(css3的整个操作是对图层的组合来实现的,所以不会引发重绘重排),而后者会额外触发页面的重排和重绘;
  • 尽量让动画元素脱离文档流(document flow)中,以减少重排(reflow);
  • 操作DOM的js语句能连着写尽量连着写,这样可借助浏览器的优化策略,将可触发重排的操作放于一个队列中,然后一次性进行一次重排;如果操作DOM的语句中间被其他诸如赋值语句之类的间断了,页面可能就会发生多次重排了。
  • 不会频繁变化的元素属性值,读取一次后应进行存储,不要重复读取。
  • 将多次对样式的操作合并成一次:不要多次修改样式,可以预先定义好class,直接修改DOM的className,这样只会引发一次重排重绘。
  • 将dom隐藏后修改然后再显示:如果要对dom元素进行多次操作,首先将dom设置为不可见,然后再对dom操作,操作完成后再将dom元素设置为可见,这样只会有两次重排重绘。
  • 利用文档碎片 documentFragment:documentFragment 不是真实 dom树的一部分,它的变化不会触发dom树的重新渲染,且不会导致性能等问题,将创建的新元素全部添加到documentFragment上,最后让documentFragment一起插入到dom元素中。
var element  = document.getElementsByTagName('body')[0]; // assuming ul exists
var fragment = document.createDocumentFragment();
var browsers = ['Firefox', 'Chrome', 'Opera',
    'Safari', 'Internet Explorer'];

browsers.forEach(function(browser) {
    var li = document.createElement('li');
    li.textContent = browser;
    fragment.appendChild(li);
});

element.appendChild(fragment);

 

参考资料

赞(0) 打赏
文章名称:《CSS性能:重排、重绘、重组》
文章链接:https://www.orzzone.com/css-performance.html
商业联系:yakima.public@gmail.com

本站大部分文章为原创或编译而来,对于本站版权文章,未经许可不得用于商业目的,非商业性转载请以链接形式标注原文出处。
本站内容仅供个人学习交流,不做为任何投资、建议的参考依据,因此产生的问题需自行承担。

评论 抢沙发

觉得文章有用就打赏一下文章作者

非常感谢你的打赏,我们将继续给力提供更多优质内容!

支付宝扫一扫打赏

微信扫一扫打赏

登录

找回密码

注册