大家好,我是小七,今天想从实战角度来教大家如何使用css进行性能优化,本文将用代码和performance来进行分析,我们应该如何写出高性能的css。
废话不多说,进入今天的主题:如何写出高质量的CSS
说道这个话题,就不得不提我们css的回流和重绘,想从css角度进行性能优化,就一定少不了这两个罪魁祸首,那么我们应该怎么减少回流和重绘。
答案当然就是开启GPU加速, 我们都知道计算机的GPU是用于3D绘画的,所以它针对于浏览器的绘制一定会有提升,我们可以利用GPU的特性来托管网页中的部分元素来减少回流和重绘,这样就可以大幅度减少页面的回流重绘次数。
那么怎么可以减少回流和重绘?
有一个场景,需要我们写一个200px * 200px的盒子,盒子需要有个动画,上下移动200px。 如果我们使用margin进行编写,代码如下:
@keyframes identifier {
from {
margin-top: 0;
}
to {
margin-top: 200px;
}
}
.box {
background: red;
width: 200px;
height: 200px;
animation: 2s identifier infinite;
}
我们利用浏览器的监控软件可以看到,当前动画会导致每秒将近60次的回流和重绘。
接下来我们在使用performance来分析一下是不是这样的。
可以看到我们每16.7ms进行一次layout和paint,至于为什么是16.7ms,是因为我们的浏览器大概1s的时间绘制60帧, 换算下来就是大概1帧16.7ms。
所以我们每秒就要回流重绘60次,简直是非常的浪费性能,如果我们使用定位开启另一个图层,会是什么结果呢?
@keyframes identifier {
from {
top: 0;
}
to {
top: 200px;
}
}
.box {
background: red;
width: 200px;
height: 200px;
position: absolute;
animation: 2s identifier infinite;
}
可以看到结果还是一样的,所以脱离文档流并不能帮我们规避重排和重绘,但是会不会提升性能呢?
当然是可以的,具体原因是为什么呢?我们可以观察一下下面的代码:
@keyframes identifier {
from {
margin-top: 0;
}
to {
margin-top: 200px;
}
}
.box {
background: red;
width: 200px;
height: 200px;
animation: 2s identifier infinite;
}
但是这次我们在盒子下面添加了一些其他的元素。
我们可以看到页面的一些指标,接下来我们脱离文档流再来看一下:
@keyframes identifier {
from {
top: 0;
}
to {
top: 200px;
}
}
.box {
background: red;
width: 200px;
height: 200px;
position: absolute;
z-index: 10;
animation: 2s identifier infinite;
}
可以看到我们的性能都有了一些提升那是因为什么呢?
我们具体分析一下两种方式的回流情况:
margin-top方式:
我们发现回流阶段用了0.12ms
脱离文档流的方式:
而这种方式,我们发现回流阶段只用了61微秒。
所以我们发现元素脱离了文档流之后, 回流和重绘只会发生在自己所在的图层,并不会影响其他图层。
所以我们合理利用BFC可以提升页面的性能。
但是今天的主角并不是他,我们继续介绍一种方式,可以完全避免回流和重绘。
@keyframes identifier {
from {
transform: translate3d(0,0,0);
}
to {
transform: translate3d(0,200px,0);
}
}
.box {
background: red;
width: 200px;
height: 200px;
animation: 2s identifier infinite;
}
我们来看下这种情况下的性能如何呢?
我们发现并不会发生回流和重绘,我们在打开performance来看一下
我们也发现干干净净的并没有发生回流和重绘。
那为什么使用transform3D就不会发生回流和重绘呢?
浏览器接收到页面文档后,会将文档中的标记语言解析为DOM树。DOM树和CSS结合后形成浏览器构建页面的渲染树。渲染树中包含了大量的渲染元素,每一个渲染元素会被分到一个图层中,每个图层又会被加载到GPU形成渲染纹理,而图层在GPU中transform 是不会触发 repaint 的,最终这些使用 transform 的图层都会由独立的合成器进程进行处理。
在我们的示例中,CSS transform 创建了一个新的复合图层,可以被GPU直接用来执行 transform 操作。在chrome开发者工具中开启“show layer borders”选项后,每个复合图层就会显示一条黄色的边界。
3D 和 2D transform 的区别就在于,浏览器在页面渲染前为3D动画创建独立的复合图层,而在运行期间为2D动画创建。动画开始时,生成新的复合图层并加载为GPU的纹理用于初始化 repaint。然后由GPU的复合器操纵整个动画的执行。最后当动画结束时,再次执行 repaint 操作删除复合图层。
上面代码的作用就是让浏览器执行 3D transform。浏览器通过该样式创建了一个独立图层,图层中的动画则有GPU进行预处理并且触发了硬件加速。
使用硬件加速并不是十全十美的事情,比如:
- 内存。如果GPU加载了大量的纹理,那么很容易就会发生内容问题,这一点在移动端浏览器上尤为明显,所以,一定要牢记不要让页面的每个元素都使用硬件加速。
- 使用GPU渲染会影响字体的抗锯齿效果。这是因为GPU和CPU具有不同的渲染机制。即使最终硬件加速停止了,文本还是会在动画期间显示得很模糊。
总结: 适当的使用GPU加速可以帮助我们更好的提升页面性能,减少回流重绘造成的性能损失。