最小化重绘和重排

const e1 = document.getElementById('test');
e1.style.padding = '5px';
e1.style.borderLeft = '1px';
e1.style.borderRight = '2px';

上面例子中,三个样式被修改(几何结构改变),每一个都会引起回流。当然,大部分浏览器都做了优化,只会触发一次重排。但是如果旧版浏览器浏览或者在上面代码执行时,有其他代码访问了布局信息(上文中会触发回流的布局信息),那么就会导致三次重排

我们可以合并所有的改变然后一次处理,比如可以采取以下方式:

1.使用cssText

const e1 = document.getElementById('test');
e1.style.cssText += 'border-left: 1px; border-right: 2px; padding: 5px'

cssText的使用优势

一般情况下我们用js设置元素对象的样式会使用这样的形式:

var element= document.getElementById(“id”);
           element.style.width=”20px”;
           element.style.height=”20px”;
           element.style.border=”solid 1px red”;

样式一多,代码就很多;而且通过JS来覆写对象的样式是比较典型的一种销毁原样式并重建的过程,这种销毁和重建,都会增加浏览器的开销。

js中有一个cssText的方法:

语法为:

obj.style.cssText=”样式”;

element.style.cssText=”width:20px;height:20px;border:solid 1px red;”;

这样就可以尽量避免页面reflow,提高页面性能。

但是,这样会有一个问题,会把原有的cssText清掉,比如原来的style中有’display:none;’,那么执行完上面的JS后,display就被删掉了。
为了解决这个问题,可以采用cssText累加的方法

Element.style.cssText += ‘width:100px;height:100px;top:100px;left:100px;’

因此,上面cssText累加的方法在IE中是无效的。

最后,可以在前面添加一个分号来解决这个问题:

Element.style.cssText += ‘;width:100px;height:100px;top:100px;left:100px;’

再进一步,如果前面有样式表文件写着 div { text-decoration:underline; },这个会被覆盖吗?不会!因为它不是直接作用于 HTML 元素的 style 属性。

2.修改css的class

const e1 = document.getElementById('test');
e1.className += 'active'

需要提前写好active的样式

 

批量修改dom

当需要对dom进行一系列修改的时候,可以通过以下步骤减少回流重绘次数:

1.使元素脱离文档流

2.对其进行多次修改

3.将元素带回到文档中

该过程第一和第三步可能会引起回流,但经过第一步之后,对dom的所有修改都不会引起回流,因为他已经不在渲染树中了。

有三种方式可以让dom脱离文档流:

  • 隐藏元素,应用修改,重新显示
  • 使用文档碎片(document fragment)在当前dom之外构建一个子树,再把它拷贝回文档。
  • 将原始元素拷贝到一个脱离文档的节点中,修改节点后,再替换原始的元素

假设我们要执行一段批量插入节点的代码:

function appendDataToElement (appendToElement, data) {
  let li;
  for (let i = 0; i < data.length; i ++) {
    li = document.createElement('li');
    li.textContent = 'text'
    appendToElement.appendChild(li)
  }
}

const u1 = document.getElementById('list')
appendDataToElement(u1, data)

1.隐藏元素,应用修改,重新显示

这样在显示和隐藏节点的时候,产生两次重绘:

function appendDataToElement (appendToElement, data) {
  let li;
  for (let i = 0; i < data.length; i ++) {
    li = document.createElement('li');
    li.textContent = 'text'
    appendToElement.appendChild(li)
  }
}

const u1 = document.getElementById('list')
u1.style.display = 'none';
appendDataToElement(u1, data);
u1.style,display = 'block';

注意display属性是block

2.使用文档片段(document fragment)在当前dom之外构建一个子树,再把它拷贝回文档。

const u1 = document.getElementById('test')
const fragment = document.createDocumentFragment();
appendDataToElement(fragment, data);
u1.appendChild(fragment);

3.将原始元素拷贝到一个脱离文档的节点中,修改节点后,再替换原始的元素

const u1 = document.getElementById('list');
const clone = u1.cloneNode(true);
appendDataToElement(clone, data)
u1.parentNode.replaceChild(clone, u1);

对于上述那种情况,我写了一个demo来测试修改前和修改后的性能。然而实验结果不是很理想。

原因:原因其实上面也说过了,浏览器会使用队列来储存多次修改,进行优化,所以对这个优化方案,我们其实不用优先考虑。

避免触发同步布局事件

上文我们说过,当我们访问元素的一些属性的时候,会导致浏览器强制清空队列,进行强制同步布局。举个例子,比如说我们想将一个p标签数组的宽度赋值为一个元素的宽度,我们可能写出这样的代码:

function initP() {
  for (let i = 0; i < paragraphs.length; i++) {
        paragraphs[i].style.width = box.offsetWidth + 'px';
  }
}

这段代码看上去是没有什么问题,可是其实会造成很大的性能问题。在每次循环的时候,都读取了box的一个offsetWidth属性值,然后利用它来更新p标签的width属性。这就导致了每一次循环的时候,浏览器都必须先使上一次循环中的样式更新操作生效,才能响应本次循环的样式读取操作。每一次循环都会强制浏览器刷新队列。我们可以优化为:

const width = box.offsetWidth;
function initP () {
  for (let i = 0; i < paragraphs.length; i ++){
    paragraphs[i].style.width = width + 'px'
  }
}

with + 'px' 这个细节也很巧妙

对于复杂动画效果,使用绝对定位让其脱离文档流

对于复杂动画效果,由于会经常的引起回流重绘,因此,我们可以使用绝对定位,让它脱离文档流。否则会引起父元素以及后续元素频繁的回流。这个我们就直接上个例子。

打开这个例子后,我们可以打开控制台,控制台上会输出当前的帧数(虽然不准)。

从上图中,我们可以看到,帧数一直都没到60。这个时候,只要我们点击一下那个按钮,把这个元素设置为绝对定位,帧数就可以稳定60。

css3硬件加速(GPU加速)

比起考虑如何减少回流重绘,我们更期望的是,根本不要回流重绘。这个时候,css3硬件加速就闪亮登场啦!!

划重点:使用css3硬件加速,可以让transform、opacity、filters这些动画不会引起回流重绘 。但是对于动画的其它属性,比如background-color这些,还是会引起回流重绘的,不过它还是可以提升这些动画的性能。

本篇文章只讨论如何使用,暂不考虑其原理,之后有空会另外开篇文章说明。

1、如何使用

常见的触发硬件加速的css属性:

  • transform
  • opacity
  • filters
  • Will-change

3、重点

使用css3硬件加速,可以让transform、opacity、filters这些动画不会引起回流重绘。

对于动画的其它属性,比如background-color这些,还是会引起回流重绘的,不过它还是可以提升这些动画的性能。

4、css3硬件加速的坑

如果你为太多元素使用css3硬件加速,会导致内存占用较大,会有性能问题。

在GPU渲染字体会导致抗锯齿无效。这是因为GPU和CPU的算法不同。因此如果你不在动画结束的时候关闭硬件加速,会产生字体模糊。

总结

本文主要讲了浏览器的渲染过程、浏览器的优化机制以及如何减少甚至避免回流和重绘,希望可以帮助大家更好的理解回流重绘。

你可能感兴趣的:(css,html,js)