前言:
浏览器渲染展示网页的过程,大致分为以下几个步骤:
- 解析HTML(HTML Parser)
- 构建DOM树(DOM Tree)
- 渲染树构建(Render Tree)
- 绘制渲染树(Painting)
对于网站来说,性能至关重要,CSS作为页面渲染和内容展现的重要环节,影响着用户对整个网站的第一体验。因此,与其相关的性能优化是不容忽视的。
实践型的优化技巧:
内联首屏关键CSS(Critical CSS):
性能优化中有一个重要的指标——首次有效绘制(First Meaningful Paint,简称FMP)即指页面的首要内容(primary content)出现在屏幕上的时间。这一指标影响用户看到页面前所需等待的时间。
异步加载CSS:
CSS会阻塞渲染,在CSS文件请求、下载、解析完成之前,浏览器将不会渲染任何已处理的内容。有时这种阻塞是必须的,我们希望在所需的CSS加载之后,浏览器才开始渲染页面。首屏关键CSS内联后,剩余的CSS内容的阻塞渲染就不是必需的了,可以使用外部CSS,并且异步加载。
文件压缩:
文件的大小会直接影响浏览器的加载速度,这一点在网络较差时表现地尤为明显。相信大家都早已习惯对CSS进行压缩,现在的构建工具,如webpack、gulp/grunt、rollup等也都支持CSS压缩功能。压缩后的文件能够明显减小,可以大大降低了浏览器的加载时间。
去除无用CSS:
删除代码中无用的CSS,一种是不同元素或者其他情况下的重复代码,一种是整个页面内没有生效的CSS代码。
提高代码的复用性:
可以对一些公共变量或者公共样式进行抽取,不要写重复代码,减少代码体积。
建议型的技巧:
慎重选择高消耗的样式:
高消耗属性在绘制前需要浏览器进行大量计算:
- box-shadows
- border-radius
- transparency(透明度)
- transforms
- CSS filters(滤镜,性能杀手)
- :nth-child等
有选择地使用选择器:
虽然浏览器在这一方面做了很多优化,不同选择器的性能差别并不明显,但是我们日常使用中还是得多加注意:
- 避免使用通用选择器。
- 避免使用标签或 class 选择器限制 id 选择器。
- 避免使用标签限制 class 选择器。
- 避免使用多层标签选择器。使用 class 选择器替换,减少css查找。
- 避免使用子选择器。
- 使用继承,减少代码重复编写。
避免过分重排(Reflow):
浏览器为了重新渲染部分或整个页面,重新计算页面元素位置和几何结构的进程叫做reflow,页面上任何一个节点触发来reflow,会导致他的子节点和祖先节点重新渲染<重排会导致浏览器重新计算整个文档,重新构建渲染树,这一过程会降低浏览器的渲染速度。
- 不要分别修改 DOM 的样式,预先定义好 class,然后修改 DOM 的 className。
- 若某个DOM频繁改动,可以考虑将操作合并成一步或者将DOM离线后(display:none)再操作。
- 不要把 DOM 结点的属性值放在一个循环里当成循环里的变量。
- 尽可能不要修改影响范围比较大的 DOM。
- 动画的元素使用绝对定位 absolute / fixed(脱离文档流,减少对其他元素影响)。
- 避免使用 table 布局,小改动会造成整个 table 的重新布局。
避免过分重绘(Repaints):
当元素改变的时候,将不会影响元素在页面当中的位置(比如 background-color, border-color, visibility),浏览器仅仅会应用新的样式重绘此元素,此过程称为 Repaint。
虽然浏览器对重排重绘做了相应优化,某些情况下会将多次的重排、重绘操作队列合并为一次执行,不过我们也应该从源头上避免过度的重排重绘。
不要使用@import:
- 使用@import引入CSS会影响浏览器的并行下载。使用@import引用的CSS文件只有在引用它的那个css文件被下载、解析之后,浏览器才会知道还有另外一个css需要下载,这时才去下载,然后下载后开始解析、构建render tree等一系列操作,导致浏览器无法并行下载所需的样式文件。
- 多个@import会导致下载顺序紊乱。在IE中,@import会引发资源文件的下载顺序被打乱,即排列在@import后面的js文件先于@import下载,并且打乱甚至破坏@import自身的并行下载。
不滥用 Float:
Float在渲染时计算量比较大,尽量减少使用。
正确使用 Display 的属性:
Display 属性会影响页面的渲染,请合理使用:
- display: inline后不应该再使用 width、height、margin、padding 以及 float。
- display: inline-block 后不应该再使用 float。
- display: block 后不应该再使用 vertical-align。
- display: table-* 后不应该再使用 margin 或者 float。
动画性能优化:
动画的实现原理,是利用了人眼的“视觉暂留”现象,在短时间内连续播放数幅静止的画面,使肉眼因视觉残象产生错觉,而误以为画面在“动”。
基本概念:
- 帧:在动画过程中,每一幅静止画面即为一“帧”。
- 帧率:即每秒钟播放的静止画面的数量,单位是fps(Frame per second)。
- 帧时长:即每一幅静止画面的停留时间,单位一般是ms(毫秒)。
- 跳帧(掉帧/丢帧):在帧率固定的动画中,某一帧的时长远高于平均帧时长,导致其后续数帧被挤压而丢失的现象。
一般浏览器的渲染刷新频率是 60 fps,所以在网页当中,帧率如果达到 50-60 fps 的动画将会相当流畅,让人感到舒适。
使用动画时可留意:
- 使用基于 javaScript 的动画,尽量使用 requestAnimationFrame. 避免使用 setTimeout, setInterval。
- 避免通过类似 jQuery animate()-style 改变每帧的样式,使用 CSS 声明动画会得到更好的浏览器优化。
- 使用 translate 取代 absolute 定位就会得到更好的 fps,动画会更顺滑。
- 多利用硬件能力,如通过 3D 变形开启 GPU 加速。
拓展:
GPU(Graphics Processing Unit) 是图像处理器。GPU 硬件加速是指应用 GPU 的图形性能对浏览器中的一些图形操作交给 GPU 来完成,因为 GPU 是专门为处理图形而设计,所以它在速度和能耗上更有效率。
GPU 加速可以不仅应用于3D,而且也可以应用于2D。这里, GPU 加速通常包括以下几个部分:Canvas2D,布局合成(Layout Compositing), CSS3转换(transitions),CSS3 3D变换(transforms),WebGL和视频(video)。
例子:
/*
* 将 2d transform 换成 3d
* 就可以强制开启 GPU 加速
* 提高动画性能
*/
div {
transform: translate(10px, 10px);
}
div {
transform: translate3d(10px, 10px, 0);
}
注意:开启硬件加速相应的也会有额外的开销,消耗更多的内存和功耗。
总结:
- 提高加载性能。
- 提高选择器性能。
- 提高渲染性能。
- 提高代码可维护性、健壮性。