消除阻塞渲染的 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
文件就可以立即向用户显示页面内容,而无需等待其他两个文件加载完成。
对于使用 media 属性的链接:
media="print"
: 这个文件中的样式只会在打印文档时应用,所以在加载页面时不需要渲染,也不会阻塞首次渲染。media="(max-width: 568px)"
: 这些样式只会在设备宽度为max-width=568px
时应用,在桌面/平板设备上加载页面时不会阻塞首次渲染。
使用 media 属性,我们可以在特定情况下调整页面显示,比如渲染后、调整屏幕大小、改变设备方向(横向/纵向)等。
media 的值必须是 媒体类型 或 媒体查询,这在加载外部样式表时非常有用 - 它可以帮助浏览器选择首次渲染所需的 CSS。
合并 CSS 或内联 CSS
一个有效的方法是,如果 CSS 文件不是太大,可以直接将 CSS 放在文档头部的 style
标签中。这种方法能很好地提升性能,因为它只需要在 DOM 加载完成后就能立即显示。

Performance API
现在让我们使用 Chrome Performance API 来测量应用了避免阻塞渲染 CSS 技术后的页面渲染时间。
我提供了一个简单的示例来帮助你理解阻塞渲染 CSS:https://hta218.github.io/render-blocking-css-example/
在这个示例中,我加载了两个 CSS 文件,并比较了在设备宽度大于和小于 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 {
// Create PerformanceObserver instance
let perfObsever = new PerformanceObserver((perf) => {
let perfEntries = perf.getEntriesByType('paint')
perfEntries.forEach(({ name, startTime }) => {
// Get the result inside this callback
console.log(`The time to ${name} was ${startTime} milliseconds.`)
})
})
// observe "paint" event
perfObsever.observe({ entryTypes: ['paint'] })
} catch (err) {
console.error(err)
}
} else {
// Remember to check the browser compatibility before using this API
console.log("Performance API isn't supported!")
}
Performance API 有许多不同的性能指标,在这个例子中,我使用了 PerformancePaintTiming。
为了更清楚地看到结果,你可以打开 Chrome DevTools,通过限制网络速度和 CPU 性能来模拟用户设备的实际情况。
对于屏幕宽度 >800px
的情况(在首次页面渲染前加载所有 CSS):

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

用户可以提前 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,比如使用媒体类型或媒体查询、合并 CSS 以及内联 CSS。 这些改变看似微小,但对性能有着重大影响。