CSS 性能优化

1. 首次有效绘制【内联首屏关键 CSS】

首次有效绘制(FMP) 指页面的首要内容出现在屏幕上的时间。这一性能指标影响用户首次看到页面的时间。

大家一般都习惯于通过 < link /> 标签来引用外部的 CSS 文件。

内联首屏关键 CSS的原理就是,将首屏渲染所需的 CSS 样式内联到 HTML 文件中,这样就可以将页面渲染的时间提前。但是 也不能把所有的 CSS 都内联到 HTML 文件中,因为初始拥塞窗口存在限制(通常是 14.6KB),如果内联CSS 后的文件超出了这一限制,系统就需要在服务器和浏览器之间进行更多次的往返,这样反而背道而驰了!且 CSS也不能进行缓存。

好,那怎么能确定哪些样式是首屏渲染需要的呢?除了手动识别,github上有一个项目Critical CSS4,可以将属于首屏的关键样式提取出来。

2. 异步加载 CSS

由于CSS 会阻塞渲染,在 CSS 文件请求、下载、解析完成前,浏览器不会渲染任何已经加载的内容。因为我们不想出现"白屏"效果,即我们不想只渲染没有样式的 DOM。但是,如果我们将首屏关键 CSS 内联后,剩余的 CSS 其实就不是必须一定在页面开始渲染之前必须加载完毕的了!【即可以将剩余的CSS进行异步加载】

ini 复制代码
// 创建link标签 const myCSS = document.createElement( "link" );
myCSS.rel = "stylesheet"; 
myCSS.href = "mystyles.css"; // 插入到header的最后位置
document.head.insertBefore( myCSS, document.head.childNodes[ document.head.childNodes.length - 1 ].nextSibling );

<2> 异步加载(二) 【对浏览器来说,如果样式表不适用于当前媒体类型,其优先级会被放低,会在不阻塞页面渲染的情况下再进行下载】

注意: 加载完毕后,还要把 rel 属性改为 stylesheet,这样浏览器才会帮我们解析!

ini 复制代码
<link rel="stylesheet" href="mystyles.css" media="noexist" onload="this.media='all'">

这样方法太麻烦了!!

<3> 异步加载(三) 【使用 preload 比使用不匹配的media方法能够更早地开始加载CSS】

ini 复制代码
<link rel="stylesheet" href="/path/to/my.css" media="print" onload="this.media='all'; this.onload=null;">

注意as是必须的。忽略as属性,或者错误的as属性会使preload等同于XHR请求,浏览器不知道加载的是什么内容,因此此类资源加载优先级会非常低。as的可选值可以参考上述标准文档。

虽然 ,浏览器现在支持度不算高,但是可以通过loadCSS 进行 polyfill !

3. 文件压缩 【最常用】

文件的大小会直接影响浏览器的加载速度,这一点在网络较差时表现地尤为明显。相信大家都早已习惯对CSS进行压缩,现在的构建工具,如webpack、gulp/grunt、rollup等也都支持CSS压缩功能。压缩后的文件能够明显减小,可以大大降低浏览器的加载时间。

4. 去除无用的 CSS

对于无用的 CSS 代码,一般会有两类:

(1) 不同元素的重复样式

对于此类,需要我们编码的时候尽可能地提取公共类,减少重复,实现代码复用!

(2) 没有生效的样式

对于此类,那就是要删除了,如果手动删的话,就效率太低了。可以借助 Uncss 库来实现!Uncss可以用来移除样式表中的无用CSS,并且支持多文件和 JavaScript 注入的CSS。

5. 优化回流和重绘

在网站的使用过程中,某些操作会导致样式的改变,这时浏览器会检测到这些改变并重新渲染!其中有些操作所耗费的性能还挺多。我们都知道,当FPS为60时,用户使用网站时才会感到流畅。这也就是说,我们需要在16.67ms内完成每次渲染相关的所有操作,所以我们要尽量减少耗费更多的操作。

(1) 回流(重排)

重排 会导致浏览器重新计算整个文档,重新构建渲染树,这一过程会降低浏览器的渲染速度。如下所示,有很多操作会触发重排,我们应该避免频繁触发这些操作。

  • 改变盒模型属性

    • width、height、padding、margin、display、border、box-sizing
  • 改变定位属性

    css 复制代码
    - position、top、bottom、left、right、float、clear
  • 改变文档流属性

    css 复制代码
    - overflow、text-align、vertical-align、line-height、word-wrap、white-space、float、clear、display
  • 改变字体属性

    • font-size、font-weight、font-family、text-decoration、text-transform、text-shadow、line-height

我们可以通过CSS Trigger15查询哪些属性会触发重排与重绘。

注意:

  • 布局可以多使用 flex 布局,因为flex比使用inline-blockfloat时重排更快!
  • 元素移动时,可以多使用 translate ,因为translate不会引起回流!

(2) 重绘

当元素的外观(如color,background,visibility等属性)发生改变时,会触发重绘。在网站的使用过程中,重绘是无法避免的 。不过,浏览器对此做了优化,它会将多次的重排、重绘操作合并为一次执行。不过我们仍需要避免不必要的重绘

(3) 避免不必要的回流和重绘

  • 样式修改 合并对 DOM 样式的修改,借助 CSS 的 class 实现
ini 复制代码
const el = document.querySelector('.box') 
el.style.margin = '5px' el.style.borderRadius = '12px' 
el.style.boxShadow = '1px 3px 4px #ccc'

改为使用 class :

css 复制代码
.update{
margin: 5px; 
border-dadius: 12px; 
box-shadow: 1px 3px 4px #ccc 
}
const el = document.querySelector('.box') 
el.classList.add('update')
  • 访问 DOM 尽量减少对 DOM 进行多次访问【如想要获得宽高...】,可以使用变量将 DOM 信息进行存储!

  • 操作 DOM 可以对节点进行克隆,在克隆节点上进行操作后,再去替换原节点

ini 复制代码
const el = document.querySelector('.box') 
const fruits = ['front', 'nanjiu', 'study', 'code']; 
const cloneEl = el.cloneNode(true)
fruits.forEach(item => { 
const li = document.createElement('li'); 
li.innerHTML = item;
cloneEl.appendChild(li);
}); 
el.parentElement.replaceChild(cloneEl,el)

6. 编码时的小技巧:

(1) 选择器使用方面:

  • 不要使用嵌套过多较复杂的选择器
  • 尽量不使用通配符和标签选择器【需要匹配的元素太多】

(2) 不要使用 @import 【不要使用@import引入CSS】

原因:

  • @import 会影响浏览器的并行下载
  • 多个@import会导致下载顺序紊乱
相关推荐
江号软件分享35 分钟前
有效保障隐私,如何安全地擦除电脑上的敏感数据
前端
web守墓人2 小时前
【前端】ikun-markdown: 纯js实现markdown到富文本html的转换库
前端·javascript·html
Savior`L2 小时前
CSS知识复习5
前端·css
许白掰2 小时前
Linux入门篇学习——Linux 工具之 make 工具和 makefile 文件
linux·运维·服务器·前端·学习·编辑器
中微子6 小时前
🔥 React Context 面试必考!从源码到实战的完整攻略 | 99%的人都不知道的性能陷阱
前端·react.js
Liudef067 小时前
儿童趣味记忆配对游戏
css·游戏·css3
中微子7 小时前
React 状态管理 源码深度解析
前端·react.js
加减法原则8 小时前
Vue3 组合式函数:让你的代码复用如丝般顺滑
前端·vue.js
yanlele9 小时前
我用爬虫抓取了 25 年 6 月掘金热门面试文章
前端·javascript·面试
lichenyang4539 小时前
React移动端开发项目优化
前端·react.js·前端框架