优化网页字体

在之前的模块中,您学习了如何优化 HTML、CSS、JavaScript 和媒体资源。在本模块中,您将了解一些优化 Web 字体的方法。

Web 字体可能会在加载时间和渲染时间影响网页性能。 大型字体文件可能需要一段时间才能下载完毕,并会对 First Contentful Paint (FCP) 产生负面影响,而错误的 font-display可能会导致不必要的布局偏移,从而影响网页的累积布局偏移 (CLS)

在讨论如何优化 Web 字体之前,了解浏览器如何发现 Web 字体会很有帮助,这样您就可以了解 CSS 如何在某些情况下防止检索不必要的 Web 字体。

发现

网页的 Web 字体在样式表中使用 @font-face 声明进行定义:

@font-face {
  font-family: "Open Sans";
  src: url("/fonts/OpenSans-Regular-webfont.woff2") format("woff2");
}

上述代码段定义了一个名为 "Open Sans"font-family,并告知浏览器在哪里可以找到相应的 Web 字体资源。为了节省带宽,浏览器不会下载网络字体,直到确定当前页面的布局需要该字体时才会下载。

h1 {
  font-family: "Open Sans";
}

在上面的 CSS 代码段中,浏览器会在解析网页 HTML 中的 <h1> 元素时下载 "Open Sans" 字体文件。

preload

如果您的 @font-face 声明是在外部样式表中定义的,则浏览器只有在下载完样式表后才能开始下载这些声明。这使得 Web 字体成为后期发现的资源,但我们可以采取一些方法来帮助浏览器更早地发现 Web 字体。

您可以使用 preload 指令提前请求 Web 字体资源。preload 指令可让浏览器在网页加载期间尽早发现 Web 字体,并立即开始下载这些字体,而无需等待样式表完成下载和解析。preload 指令不会等到网页需要字体时才开始加载。

<!-- The `crossorigin` attribute is required for fonts—even
     self-hosted ones, as fonts are considered CORS resources. -->
<link rel="preload" as="font" href="/fonts/OpenSans-Regular-webfont.woff2" crossorigin>

内嵌 @font-face 声明

您可以使用 <style> 元素将渲染阻塞 CSS(包括 @font-face 声明)内嵌到 HTML 的 <head> 中,从而使字体在网页加载期间更早被发现。在这种情况下,浏览器会在页面加载过程中更早地发现 Web 字体,因为它无需等待下载外部样式表。

与使用 preload 提示相比,内嵌 @font-face 声明的优势在于,浏览器只会下载渲染当前页面所需的字体。这样可以消除下载未使用的字体的风险。

下载

在发现网络字体并确保当前页面的布局需要这些字体后,浏览器便可以下载它们。网络字体的数量、编码和文件大小会显著影响浏览器下载和呈现网络字体的速度。

自行托管网页字体

网络字体可以通过 Google Fonts 等第三方服务提供,也可以自行托管在您的来源上。使用第三方服务时,网页需要先打开与提供商网域的连接,然后才能开始下载所需的网络字体。这可能会延迟发现和后续下载网络字体。

可以使用 preconnect 资源提示来减少此开销。通过使用 preconnect,您可以告知浏览器比平时更早地打开与跨源的连接:

<link rel="preconnect" href="https://fonts.googleapis.com">  
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>

上述 HTML 代码段会提示浏览器建立与 fonts.googleapis.com 的连接以及与 fonts.gstatic.comCORS 连接。某些字体提供商(例如 Google Fonts)会从不同的来源提供 CSS 和字体资源。

通过自行托管 Web 字体,您可以无需使用第三方连接。在大多数情况下,自行托管 Web 字体比从跨源下载字体更快。如果您打算自行托管 Web 字体,请检查您的网站是否使用内容分发网络 (CDN)HTTP/2 或 HTTP/3,并为网站所需的 Web 字体设置正确的缓存标头。

使用 WOFF2(且仅使用 WOFF2)

WOFF2 获得了广泛的浏览器支持,并且压缩效果最好,比 WOFF 提高了 30%。文件大小减小后,下载时间也会缩短。WOFF2 格式通常是唯一需要的格式,可确保在现代浏览器中实现完全兼容。

对网页字体进行子集化

网页字体通常包含各种不同的字形,这些字形对于表示不同语言中使用的各种字符至关重要。如果您的网页仅以一种语言提供内容,或者仅使用一种字母表,那么您可以通过子集化来减小 Web 字体的尺寸。这通常通过指定一个或一系列 Unicode 代码点来实现。

子集是原始 Web 字体文件中包含的字形的缩减集。例如,您的网页可能不会提供所有字形,而是仅提供拉丁字符的特定子集。根据所需子集,移除字形可以显著减小字体文件的大小。

某些网络字体提供商(例如 Google Fonts)会通过使用查询字符串参数自动提供子集。例如,https://fonts.googleapis.com/css?family=Roboto&subset=latin 网址提供了一个样式表,其中包含仅使用拉丁字母的 Roboto 网络字体。

如果您已决定自行托管 Web 字体,下一步是使用 glyphangersubfont 等工具自行生成并托管这些子集。

不过,如果您没有能力自行托管自己的网络字体,则可以通过指定一个额外的 text 查询字符串参数(仅包含网站所需的 Unicode 代码点)来对 Google Fonts 提供的网络字体进行子集化。例如,如果您的网站上有一种显示网络字体,但只需要少量字符来显示“欢迎”一词,您可以通过 Google Fonts 通过以下网址请求该子集:https://fonts.googleapis.com/css?family=Monoton&text=Welcome。如果这种极端的子集化在您的网站上很有用,那么它可以显著减少网站上单个字体的网络字体数据量。

字体呈现

浏览器发现并下载 Web 字体后,即可进行渲染。默认情况下,浏览器会阻止呈现使用 Web 字体的任何文本,直到下载完该字体为止。您可以使用 font-display CSS 属性调整浏览器的文本呈现行为,并配置在网页字体完全加载之前应显示或不应显示哪些文本。

block

font-display 的默认值为 block。使用 block 时,浏览器会阻止渲染使用指定 Web 字体的任何文本。不同浏览器的行为略有不同。Chromium 和 Firefox 会在最多 3 秒内阻止渲染,然后使用回退。Safari 会无限期地阻止,直到网页字体加载完毕。

swap

swap 是最常用的 font-displayswap 不会阻止渲染,并且会在换入指定的网络字体之前立即以回退方式显示文本。这样,您就可以立即显示内容,而无需等待下载网络字体。

不过,swap 的缺点在于,如果后备 Web 字体与 CSS 中指定的 Web 字体在行高、字距调整和其他字体指标方面差异很大,则会导致布局偏移。如果您不注意使用 preload 提示尽快加载 Web 字体资源,或者不考虑其他 font-display 值,这可能会影响网站的 CLS

fallback

font-displayfallback 值介于 blockswap 之间。与 swap 不同,浏览器会阻止字体的渲染,但仅在很短的时间内换入后备文本。不过,与 block 不同的是,阻塞期非常短。

在快速网络上,如果网页字体下载速度很快,则在网页的初始渲染中立即使用网页字体,此时使用 fallback 值效果会很好。不过,如果网络速度较慢,则在阻塞期结束后,系统会先显示后备文本,然后在网络字体到达时将其替换掉。

optional

optional 是最严格的 font-display 值,仅当网络字体资源在 100 毫秒内下载完毕时才使用该资源。如果网络字体的加载时间超过此时间,则不会在网页上使用该字体,并且浏览器会在后台下载网络字体并将其放入浏览器缓存中,同时使用当前导航的后备字体。

这样一来,由于网页字体已下载,后续的网页导航可以立即使用该字体。font-display: optional 可避免 swap 中出现的布局偏移,但如果网页字体在初始网页导航中到达得太晚,部分用户将看不到该字体。

字体演示

知识测验

浏览器何时下载 Web 字体资源(假设该资源不是通过 preload 指令提取的)?

当网页的 CSSOM 构建完成,并且确定当前布局需要 Web 字体时。
只要在样式表中发现对它的引用。

若要向所有新式浏览器提供网页字体,唯一(也是最有效)的必要格式是什么?

TTF
EOT
WOFF
WOFF2

下一篇:拆分 JavaScript 代码

了解字体优化后,您现在可以继续学习下一个模块,该模块涵盖了一个很有可能提高用户初始网页加载速度的主题,即通过代码拆分延迟加载 JavaScript。这样一来,您就可以在网页的启动阶段(用户很可能会与网页互动的一段时间)尽可能降低带宽和 CPU 争用。