尽管web页面达到2MB的性能仍然是一个热门话题,页面越平滑,用户体验越好,转化率越高!
也就是说,我对添加肤浅的CSS3动画或不考虑后果地操纵多个DOM元素感到内疚。在应用视觉效果时,浏览器中使用了两个术语:

重绘Repaints

当变化影响元素可见性而不是布局的时候会发生一次重绘,比如:opacity, background-color, visibility, 和 outline,重绘的代价很高,因为浏览器必须检查DOM中所有其他节点的可见性——一个或多个节点可能在更改的元素下面变得可见。

重排Reflows

重排对性能的影响巨大,需要重新计算所有元素的位置和尺寸,这会导致部分或整个文档的重新渲染,改变单个元素能影响所有的子节点、父节点和兄弟节点。

重绘和重排时,用户和web页面都不能做其它事情,在极端情况下,css会让js执行变慢,比如滚动不稳定、界面响应不灵敏。

触发重排途径

添加、删除或改变元素的可见性

首先是显而易见的:使用JavaScript更改DOM将导致回流。

添加、删除或改变css样式

直接应用CSS样式或更改类名都可能会改变布局。比如更改元素的宽度会影响同一DOM树及其周围的所有元素。

CSS3 animations 和 transitions

动画的每一帧都会引起回流。

用offsetWidth 和 offsetHeight

读取元素的offsetWidthoffsetHeight属性会触发回流来计算属性值。

用户行为

一些用户行为会触发回流,比如:hover、在输入框中输入文本、调整窗口大小、更改字体大小、切换样式表或字体。

回流的影响各不相同,比如相同的操作一些浏览器表现的更好,一些元素的渲染开销会更大。幸运的是,你可以使用一些通用技巧来提升性能.

通用技巧

使用最佳布局方案

不要使用内联样式和table布局!
内联样式会在下载HTML时影响布局,并触发额外的reflow。
table布局开销很大,因为解析器需要多次传递去计算单元格维度,使用table时应用fixed定位有一定的优化效果,因为列的宽度是基于标题行的内容。
主页面布局应用flexbox也会有性能影响,因为在HTML下载的时候,flex items的位置和尺寸可能会变化。

最小化CSS规则的数量

css规则越少,重排越快,要尽量避免复杂的css选择器。
如果您使用的是Bootstrap这样的框架,那么这一点尤其成问题——很少有站点使用了框架提供的所有样式。像Unused CSS、uCSS、grunt-uncss和gulp-uncss这样的工具可以显著减少样式定义和文件大小。

最小化DOM层级

稍微复杂一点——减小DOM树大小和每个分支的元素数量。文档越小越浅,回流越快。如果不需要支持古老的浏览器,可以删除不必要的包裹元素。

更改class的DOM层级要低

改变class的DOM层级尽可能的低(比如:没有多个深度嵌套的元素),这能减小重排的范围,只重排必要的元素,本质上,只有在对嵌套子节点的影响很小的情况下,才将类更改应用于父节点,比如包装器。

从文档流中移除复杂的动效

通过使用position: absolute; 或者 position: fixed;来使有动效的元素脱离文档流,这可以在不影响文档流中的其它元素的情况下更新尺寸和位置。

更新隐藏的元素

通过display: none;来隐藏的元素在改变时不会触发重绘和重排,可以的话,在元素可见之前进行更改。

批量更新元素

所有的DOM操作在同一次动作中进行能提高性能。
下面这个简单的例子会引起3次重排:

1
2
3
4
var myelement = document.getElementById('myelement');
myelement.width = '100px';
myelement.height = '200px';
myelement.style.margin = '10px';

我们可以把上面的操作减少到1次重排,这也更便于维护:

1
2
var myelement = document.getElementById('myelement');
myelement.classList.add('newstyles');
1
2
3
4
5
.newstyles {
width: 100px;
height: 200px;
margin: 10px;
}

你也可以最小化接触DOM的时间,假设你要创建这个项目列表:

  • item1
  • item2
  • item3

每次添加1个元素会触发7次重排-1次添加

    ,3次添加
  • ,3次添加li里面的文本。但是,可以使用DOM片段实现1次reflow,并先在内存中构建节点:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    var
    i, li,
    frag = document.createDocumentFragment(),
    ul = frag.appendChild(document.createElement('ul'));

    for (i = 1; i <= 3; i++) {
    li = ul.appendChild(document.createElement('li'));
    li.textContent = 'item ' + i;
    }

    document.body.appendChild(frag);

    限制受影响的元素

    避免大量元素受影响的情况。
    比如:一个选项卡内容控件,单击一个选项卡将激活不同的内容块。如果每个内容块的高度不同,则会影响周围的元素。
    可以为容器设置固定高度或从文档流中删除控件来提高性能。

    意识到平滑影响性能

    每帧移动1px看起来平滑,但是低端机会卡顿,每帧移动4px,需要1/4的重排,且可能只会稍微不那么平滑。

    用浏览器工具分析渲染问题

    所有主流浏览器都提供工具来强调重排如何影响性能。
    timeline