1、前言
当涉及到网站的性能优化时,CSS 的优化是一个非常重要的方面。如果你的 CSS 太复杂,那么你的网站可能会像一个慢吞吞的乌龟 一样缓慢地加载和渲染,影响用户的留存 、网站的转化率 以及网站的体验和传播等,所以对于 CSS 优化还是非常需要的,本文将介绍一些 CSS 性能优化的技巧,帮助你在编写 CSS 代码时提高性能。
2、前置知识
2.1 CSS 解析规则
我们一般书写 CSS 选择器都是从左往右书写的,所以自然就会以为 CSS 选择器是从左往右去匹配渲染的,但其实不是这样的,CSS 选择器是从右往左去匹配的。
css
.header div span {
color: red;
}
比如上面的 CSS 样式匹配查找过程是这样的:
- 先找到所有的
span元素。 - 从第一个
span元素开始,沿着span元素的父级元素查到div,最后沿着div的父级元素找到所有的.header,找到满足条件的节点就加入到结果集中。
2.2 CSS 的加载过程
简述浏览器的渲染过程:在拿到 html 文件后,浏览器会调用 html 解析器解析 html 文件,构建 DOM 树和 CSSOM 树,然后 DOM 树和 CSSOM 树合并成渲染树,最后浏览器会根据渲染树进行布局和绘制,最终将页面呈现给用户。
CSSOM 的构建 :当浏览器解析 html 文档时,如果遇到 <link> 或 <style> 标签,会开始下载和解析 CSS 文件,构建 CSSOM。在 CSSOM 构建完成之前,浏览器不会渲染任何已处理的内容,即使DOM已经解析完毕。
- CSS 不会阻塞
DOM的解析,但会阻塞DOM的渲染。 - CSS 不会阻塞
Javascript的下载,但会阻塞JavaScript的执行,因为JavaScript可以操作样式,所以需要等CSSOM构建完成之后,才能执行JavaScript。
所以从这里就可以得到以下优化思路:。
- 将一些体积小的首屏关键 CSS 内联到
html文件中,加快首屏渲染速度。 - 将 CSS 放在
<head>中,确保尽早加载。 - 使用媒体查询(如
media="print")使 CSS 非阻塞渲染。 - 在生产环境中,考虑使用 preload(如
<link rel="preload" as="style">)在不阻塞渲染的情况下加载非首屏的 CSS 资源。
2.3 CSS 权重优先级
CSS 选择器类型的优先级从低往高依次是:
- 类型选择器 (比如
h1)和伪元素选择器 (比如::before)。 - 类选择器 (比如
.header)、属性选择器 (比如[type="checkbox"])和伪类选择器 (比如:hover)。 - ID 选择器 (比如
#box)。 - !important:优先级最高。
2、CSS 加载性能优化
2.1. 提取公共的 CSS 文件
如果使用webpack进行项目打包,在打包阶段可以使用mini-css-extract-plugin提取公共 CSS 文件,便于缓存以及减少 CSS 请求次数。
2.2 避免使用 @import
- 使用
@import会阻塞浏览器的并行下载,导致加载速度变慢。 - 多个
@import会导致下载顺序紊乱。
2.3 压缩 CSS 文件
压缩 CSS 文件可以减小文件大小,从而加快加载速度。比如可以用optimize-css-assets-webpack-plugin配合mini-css-extract-plugin使用来优化和压缩 CSS 文件。
2.4 使用浏览器缓存
可以使用浏览器缓存来缓存 CSS 文件,从而在页面加载时加快速度。可以设置适当的缓存时间来确保文件在必要时能够更新。
2.5 使用 CDN 加速
- 使用 CDN(内容分发网络) 可以将 CSS 文件分发到全国各个
CDN节点的服务器上,用户可以就近下载,从而加快加载速度,也可以减少自己服务器的负载。 - 如果静态资源很多,可以准备多个静态服务器域名,比如域名是
static.xx.com,做成支持static0-5的 6 个域名, 也就是static0.xx.com、static1.xx.com、static2.xx.com...,每次请求时随机选一个域名地址进行请求,这样可以绕过浏览器同域名的连接数限制(一般是限制 6 个),6 个域名就可以同时发送 36 个连接请求。当然,这个域名个数不是越多越好,太分散的话又会涉及到多域名无法缓存静态资源的问题。
2.6 使用 CSS Sprite
CSS Sprite 技术就是我们常说的雪碧图 ,通过将多张小图标拼接成一张大图,能有效的减少HTTP请求数量以达到加速显示内容的技术。
2.7 CSS 样式抽离和去除无用 CSS
- 平时开发过程中可以将一些可复用的 CSS 抽离到一个单独文件中,减少 CSS 代码冗余。
- 在打包构建时可以利用一些插件去除没有用到的 CSS,相当于对 CSS 进行
Tree shaking,减少打包后体积。
2.8 合理使用内嵌 CSS
内嵌CSS 也就是通过元素的 style 来书写行内样式,比如 <div style="color: red"></div>,它的优缺点如下:
优点:
- 与使用
link标签相比,可以减少 CSS 的http请求量。
缺点:
- 增加
html文件的体积。 - 使用
link标签能很好的利用浏览器的缓存,而内联样式放在html里面,能否使用缓存需要看html文件的缓存策略。
综合它的优缺点,我们可以选择将一些体积小的首屏关键 CSS 内联到 html 文件中,加快首屏渲染速度。
3、CSS 选择器性能优化
3.1 避免使用通配符选择器
通配符选择器*符号可以匹配任何元素,但是这会导致浏览器需要遍历整个文档树来查找匹配的元素,从而降低性能。
3.2 使用子选择器代替后代选择器
后代选择器(如 .parent .child)会检查所有后代元素,而子选择器(如 .parent > .child)只检查直接子元素,匹配范围更小,性能更好。
3.3 优先使用类(Class)和 ID 选择器
类选择器(如 .box)和 ID 选择器(如 #box)匹配速度更快,因为它们直接指向特定元素。避免标签选择器(如 div)或属性选择器(如 [type="text"]),后者匹配更加宽泛,效率较低,尤其在大型 DOM 中。
3.4 避免深层嵌套的选择器
前面也说过,CSS 选择器需要从右往左去匹配的,嵌套的选择器如 .header div ul li a,因为浏览器需要遍历更多 DOM 节点。建议限制选择器深度在 3 层以内,使用更直接的类或 ID 选择器,如 .box。这能减少匹配时间。或者使用 BEM 命名规范,比如 .block__element--modifier,提高匹配效率。
另外在使用 ID 选择器的时候,前面就需要加上父级的选择器,比如不推荐用 .box #content,推荐用 #content。
4、CSS 属性性能优化
4.1 避免使用过于复杂的属性
过于复杂的属性会增加浏览器的渲染负担,从而降低性能。应该尽量使用简单的属性来实现需要的样式效果,比如下面这些属性:
- box-shadow :box-shadow 属性可以实现盒子的阴影效果,但是它会增加浏览器的计算和渲染成本。如果要实现一个简单的边框效果,可以使用
border属性来替代。
css
/* 不推荐使用 */
.box {
box-shadow: 2px 2px 2px #888;
}
/* 推荐使用 */
.box {
border: 1px solid #888;
}
- filter :filter 属性可以实现图像的滤镜效果,但是它会增加浏览器的计算和渲染成本。如果要实现一个简单的颜色变换效果,可以使用
background-color属性来替代。
css
/* 不推荐使用 */
.box {
filter: grayscale(50%);
}
/* 推荐使用 */
.box {
background-color: #ccc;
}
不过需要注意的是,这些替代方案可能会有一些局限性,无法完全取代原来的属性。因此,在使用这些替代方案时,需要根据具体的需求和情况进行选择。
4.2 避免使用不必要的属性
不必要的属性会增加 CSS 文件的大小,从而降低加载速度。应该尽量避免使用不必要的属性。
4.3 避免使用 !important
!important 会覆盖其他样式,从而增加渲染时间。而且它不容易被替换或覆盖,后期可维护性也比较差,更好的方式使用更具体、更准确的选择器来定义样式。
5、CSS 动画性能优化
5.1 使用 transform 和 opacity 属性来进行动画
使用 transform 和 opacity 属性可以减少浏览器的渲染负担,从而提高性能。
5.2 避免使用过于复杂的动画效果
过于复杂的动画效果会增加浏览器的渲染负担,从而降低性能。应该尽量使用简单的动画效果。
5.3 在动画中使用 will-change 属性
will-change 属性可以告诉浏览器哪些属性将要被改变,从而提前进行优化,减少渲染负担。
5.4 使用 requestAnimationFrame() 函数来优化动画
requestAnimationFrame() 函数可以在浏览器下一次渲染之前执行代码,从而减少渲染负担,提高性能。
6、CSS 渲染性能优化
6.1 使用 class 合并 DOM 的修改
比如我们需要根据不同情况给一个元素添加不同的样式,可能会写出如下的代码:
js
const el = document.querySelector('.box');
if (isHover) {
el.style.color = 'red';
el.style.background = 'blue';
} else {
el.style.color = 'blue';
el.style.background = 'red';
}
这时候我们可以定义两个 class 类,然后直接切换类名即可。
js
const el = document.querySelector('.box');
if (isHover) {
el.classList.remove('normal');
el.classList.add('hover');
} else {
el.classList.remove('hover');
el.classList.add('normal');
}
6.2 让 DOM 元素脱离文档流
使用 absoulte 或者 fixed 定位让 DOM 元素脱离文档流,这样在修改 DOM 元素的时候不会影响到其他 DOM 元素的布局,从而提高渲染性能。
7、总结
本文主要通过CSS 加载性能优化 、CSS 选择器性能优化 、CSS 属性性能优化 、CSS 动画性能优化 和CSS 渲染性能优化这些方向介绍了 CSS 的性能优化技巧。通过优化 CSS 性能,可以提高网站的性能和用户体验。
在实际开发中,可根据实际业务场景和需要去进行优化。