消除渲染阻塞 CSS 并使用 Performance API 测量页面渲染时间

- Published on
- /8 mins read/---
CSS 被浏览器视为渲染阻塞资源之一 - 这些资源必须在用户看到内容之前加载完成。
为什么应该避免渲染阻塞 CSS?
渲染阻塞 CSS 会减慢网站向用户显示的速度。
你的网站加载的每个 CSS 文件都会增加页面的首次绘制时间,这意味着如果你的页面必须加载大量 CSS,用户必须等待更长时间才能看到内容。

开始加载页面时,浏览器会自动加载所有 CSS 文件,无论它们是否阻塞渲染过程!
那么,如何限制渲染阻塞 CSS呢?
解决方案
如果你注意到你的页面有只在特定条件下使用的 CSS, 例如用于模态框内容的样式(用户必须点击才能打开和查看)、不是首先显示的标签页内容中的样式,或者只适用于大型显示器或移动设备的样式...
以下是一些帮助你的页面更快加载的方法。
使用 media 属性
当你想在网页中加载 CSS 时,你会像这样使用 link
标签:
<link href="style.css" rel="stylesheet" />
<link href="print.css" rel="stylesheet" />
<link href="style.mobile.css" rel="stylesheet" />
加载 HTML 后,浏览器还将加载这 3 个 CSS 文件,并且只有在所有文件加载完成后才会显示内容。
然而,print.css
仅在打印文档时使用(Ctrl/Cmd + P),而 style.mobile.css
仅用于移动设备上应用的样式。
在这种情况下,我们可以使用 media 属性。
<link href="style.css" rel="stylesheet" />
<link href="print.css" media="print" rel="stylesheet" />
<link href="style.mobile.css" media="(max-width: 568px)" rel="stylesheet" />
现在浏览器明白它只需要加载 style.css
文件就可以立即向用户显示页面内容,而无需等待其他 2 个文件加载。
对于使用 media 属性的链接:
media="print"
:此文件中的样式仅在打印文档时应用,因此不需要渲染,加载页面时不会阻塞首次渲染。media="(max-width: 568px)"
:这些样式仅在max-width=568px
的设备上应用,在桌面/平板设备上加载页面时不会阻塞首次渲染。
使用 media 属性,我们可以在特定情况下调整页面的显示,例如渲染后、调整屏幕大小、改变设备方向(横向/纵向)...
media 的值必须是 media type 或 media query,这在加载外部样式表时非常有用 - 它帮助浏览器为首次渲染选择必要的 CSS。
合并 CSS 或内联 CSS
一种有效的方法是,如果 CSS 不太大,直接将 CSS 放在文档头部的 style
标签中。这种方法可以很好地提高性能,因为它只需要在 DOM 加载后立即显示。
或者你可以限制加载过多的 CSS 文件。通常在编码时,开发人员倾向于按组件、模块等分离不同的 CSS 文件以便于管理。然而,加载许多 CSS 文件比只加载一个文件需要更长的时间。

Performance API
现在让我们使用 Chrome Perfomance API 来测量应用一些避免渲染阻塞 CSS 技术后的页面渲染时间。
我提供了一个简单的例子来帮助你理解渲染阻塞 CSS:https://hta218.github.io/render-blocking-css-example/
在这个例子中,我加载了两个 CSS 文件,并比较了在 device-width 大于和小于 800px
的屏幕上的页面渲染时间。
<link rel="stylesheet" href="tailwind.css" media="(min-width: 800px)" />
<link rel="stylesheet" href="bootstrap.css" media="(min-width: 800px)" />
要使用 Performance API,检查浏览器兼容性很重要。
if ('PerformanceObserver' in window) {
try {
// 创建 PerformanceObserver 实例
let perfObsever = new PerformanceObserver((perf) => {
let perfEntries = perf.getEntriesByType('paint')
perfEntries.forEach(({ name, startTime }) => {
// 在这个回调中获取结果
console.log(`The time to ${name} was ${startTime} milliseconds.`)
})
})
// 观察 "paint" 事件
perfObsever.observe({ entryTypes: ['paint'] })
} catch (err) {
console.error(err)
}
} else {
// 记得在使用此 API 之前检查浏览器兼容性
console.log("Performance API isn't supported!")
}
有许多不同的 Performance 指标,在这种情况下,我使用了 PerformancePaintTiming。
为了更清楚地看到结果,你可以打开 Chrome DevTools,限制网络和 CPU 速度以模拟用户设备的条件。
对于 >800px
的屏幕(在首次页面渲染之前加载所有 CSS),

我们需要超过 6 秒才能进行首次绘制(在所有 CSS 加载完成后)- 这相当于用户等待 6 秒才能看到页面内容。
对于只需要一个 CSS 文件进行首次绘制的情况(其余文件仍然加载但不用于或不需要首次渲染)

用户提前 2 秒看到内容,并且即使其他 2 个 CSS 文件尚未完成加载,也可以看到页面渲染。
使用 Performance API 测量的结果总结如下。

或者,你可以直接使用 PerformancePaintTiming API。
if (window.performance) {
let performance = window.performance;
let perfEntries = performance.getEntriesByType('paint');
perfEntries.forEach({ name, startTime } => {
console.log(`The time to ${name} was ${startTime} milliseconds.`);
});
} else {
console.log("Performance timing isn't supported.");
}
结论
我们可以清楚地看到页面渲染时间的显著差异,这直接影响了网页必须加载过多不必要 CSS 时的用户体验。 因此,我们需要非常小心这种资源。
有许多方法可以避免渲染阻塞 CSS,例如使用 media types 或 media queries、合并 CSS 和内联 CSS。 这些变化看似微小,但对性能有显著影响。