优化网络字体

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

网络字体可能会在加载时间和渲染时间影响页面性能。大型字体文件可能需要一段时间才能下载,并对首次内容绘制 (FCP)产生负面影响,而不正确的 font-display可能会导致不良的布局偏移,从而影响页面的累积布局偏移 (CLS)

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

发现

页面的网络字体在样式表中使用 @font-face 声明定义

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

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

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

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

preload

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

您可以使用 preload 指令来启动对网络字体资源的早期请求。preload 指令使网络字体在页面加载期间尽早被发现,并且浏览器会立即开始下载它们,而无需等待样式表完成下载和解析。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 声明

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

内联 @font-face 声明比使用 preload 提示更具优势,因为浏览器仅下载渲染当前页面所需的字体。这消除了下载未使用字体的风险。

下载

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

自行托管网络字体

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

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

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

前面的 HTML 代码段提示浏览器建立与 fonts.googleapis.com 的连接,以及与 CORS 连接到 fonts.gstatic.com。一些字体提供商(例如 Google 字体)从不同的来源提供 CSS 和字体资源。

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

仅使用 WOFF2 — 并且仅限 WOFF2

WOFF2 具有广泛的浏览器支持和最佳压缩率 - 比 WOFF 高出 30%。文件大小的减小缩短了下载时间。WOFF2 格式通常是现代浏览器完全兼容所需的唯一格式。

对网络字体进行子集化

网络字体通常包含各种不同的字形,这些字形是表示不同语言中使用的各种字符所必需的。如果您的页面仅以一种语言提供内容,或者仅使用单个字母表,那么您可以通过子集化来减小网络字体的大小。这通常通过指定数字或一系列 Unicode 代码点来完成。

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

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

如果您已决定自行托管网络字体,则下一步是使用 glyphhangersubfont 等工具自行生成和托管这些子集。

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

字体渲染

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

block

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

swap

swap 是使用最广泛的 font-displayswap 不会阻止渲染,而是在交换到指定的网络字体之前,立即以回退字体显示文本。这使您可以立即显示您的内容,而无需等待网络字体下载。

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

fallback

font-displayfallback 值是 blockswap 之间的一种折衷方案。与 swap 不同,浏览器会阻止字体的渲染,但仅在很短的时间内换入回退文本。但是,与 block 不同,阻止时间非常短。

在快速网络上使用 fallback 值效果很好,在快速网络上,如果网络字体下载速度很快,则网络字体是页面初始渲染时立即使用的字体。但是,如果网络速度较慢,则在阻止时间过后首先看到回退文本,然后在网络字体到达时将其换出。

optional

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

因此,后续页面导航可以立即使用网络字体,因为它已经下载。font-display: optional 避免了 swap 中看到的布局偏移,但如果网络字体在初始页面导航中到达太晚,则某些用户看不到该字体。

字体演示

测试您的知识

浏览器何时下载网络字体资源(假设未使用 preload 指令获取)?

一旦在样式表中发现对其的引用。
请重试。
当页面的 CSSOM 构建完成并且确定当前布局需要网络字体时。
正确!

将网络字体提供给所有现代浏览器唯一(也是最有效)的必要格式是什么?

WOFF2
正确!
WOFF
请重试。
TTF
请重试。
EOT
请重试。

下一步:代码拆分 JavaScript

在了解字体优化之后,您现在可以继续学习下一个模块,该模块涵盖了一个很有可能提高用户初始页面加载速度的主题,那就是通过代码拆分延迟加载 JavaScript。通过这样做,您可以使带宽和 CPU 争用在页面的启动阶段保持尽可能低的水平,用户在此期间很可能会与页面进行交互。