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进行异步加载】
<1> 异步加载(一) 使用 JS 动态创建样式表 link 元素,并插入到DOM中
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-block
和float
时重排更快! - 元素移动时,可以多使用
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会导致下载顺序紊乱