前端性能之回流与重绘(reflow && repaint)

写在金三银四之际。
由于种种缘由想要谋求新的发展,不得已翻起了尘封已久的高程书;写起了各类经典CSS布局;回顾起记忆略显模糊的几个项目。感慨着太多太多的知识点本身都不够深刻甚至缺少认识,却又不能急功近利,囫囵吞枣。
牢骚发完了,苦水吐完了,进入正题。

万恶之源——无知

回流(reflow),重绘(repaint)都是浏览器更新页面视图的方式,区别在于:javascript

  • 对于元素视觉上的改变(如改变outline,background-color,visibility等)会触发repaint;
  • 对于元素布局上的改变(增删节点,触发resize事件,修改style属性等)会触发reflow;

repaint和reflow是DOM操做影响性能的主要缘由。一个节点触发了repaint,浏览器会检查DOM Tree中其余全部节点的显示方式;一个节点触发了reflow会致使它的祖先节点,后代节点以及在它以后的节点所有reflow。reflow对性能的影响大于repaint。css

一个前端开发人员对这些概念一无所知是一件很恐怖的事情,想一想他在写代码的时候不知道哪些操做会对性能形成影响,可能会出现这种状况:前端

var toChange = document.getElementById('target');
toChange.style.background = '#333';
toChange.style.color = '#fff';
toChange.style.border = '1px solid #00f';

这无疑是能够优化的,下面咱们来看看如何减小reflow,repaint的次数。java

解决方式——控制

咱们无力去改变repaint,reflow对性能损害的程度,咱们能作的只有减小它们发生的次数。浏览器

脱离

动画时时刻刻都在操做着DOM,为了不动画使得其余节点也在时时刻刻reflow,能够将动画所在的元素设为position: fixed或者position: absolute,使其脱离文档流,这个元素reflow时不会影响其余节点的布局,虽然仍是会产生repaint,但相对来讲获得了优化。app

合并

能一次完成的操做就不要分两次。好比说添加多个节点时使用DocumentFragment:函数

var docFragm = document.createDocumentFragment();
var elem, contents;
for(var i = 0; i < textlist.length; i++) {
    elem = document.createElement('p');
    contents = document.createTextNode(textlist[i]);
    elem.appendChild(contents);
    docFragm.appendChild(elem);
}
document.body.appendChild(docFragm);

再好比像上面style那个例子,若是已知变化后样式,将这些样式写成一个class,再去改变元素的class或者直接修改cssText属性,这两种方式都将屡次reflow缩减为一次。布局

复制

对复制品进行操做也是一种解决方案,好比须要对已有节点进行DOM操做使用cloneNode():性能

var original = document.getElementById('container');
var cloned = original.cloneNode(true);
cloned.setAttribute('width', '50%');
var elem, contents;
for(var i = 0; i < textlist.length; i++) {
    elem = document.createElement('p');
    contents = document.createTextNode(textlist[i]);
    elem.appendChild(contents);
    cloned.appendChild(elem);
}
original.parentNode.replaceChild(cloned, original);

使用cloneNode()要注意的是,惟一的参数表明是否进行深复制。另外cloneNode()没法复制事件监听函数,以及表单控件的value。
又好比获取offsetWidth,getComputedStyle()这些取值操做每次都会触发reflow,在第一次调用时存起来也是复制的一种。优化

舍弃

在作好了其余优化措施的前提下想要进一步提高性能有时须要舍弃,好比减少动画帧数,即增大动画函数执行的间隔,这样动画流畅程度会下降,但整个应用的性能获得了提高。

其余
  • 减小table的使用,table形成的reflow是其余块级元素的三倍;
  • 尽可能避免改变DOM Tree中高层节点的class,减少reflow的影响;
  • 利用各类选择器规则减小待操做节点的数量;

最后,我认为理解reflow和repaint的原理及触发状况是十分重要的,在写每一行代码时都应该明确它对性能的影响。

参考文章:
REFLOWS & REPAINTS: CSS PERFORMANCE MAKING YOUR JAVASCRIPT SLOW?

Effecitve JavaScript

相关文章
相关标签/搜索