优化 Web 字体以获得 Core Web Vitals。
本文档讨论了字体的性能最佳实践。Web 字体在多种方面影响性能
- 延迟文本渲染: 如果 Web 字体尚未加载,浏览器通常会延迟文本渲染。在许多情况下,这会延迟 首次内容绘制 (FCP)。在某些情况下,这会延迟 最大内容绘制 (LCP)。
- 布局偏移: 字体交换的做法有可能 导致布局偏移 并影响 累积布局偏移 (CLS)。当 Web 字体及其后备字体在页面上占用不同空间量时,会发生这些布局偏移。
本文档分为三个部分:字体加载、字体交付和 字体渲染。每个部分都解释了字体生命周期的特定方面如何工作,并提供了相应的最佳实践。
字体加载
字体是重要的资源。没有字体,用户可能无法查看页面内容。因此,字体加载的最佳实践通常侧重于确保字体尽可能早地加载。应特别注意从第三方站点加载的字体,因为下载这些字体文件需要单独的连接设置。
如果您不确定页面的字体是否及时请求,请查看 Chrome DevTools 中 Network 面板内的 Timing 选项卡以获取更多信息。
了解 @font-face
在深入了解字体加载的最佳实践之前,务必了解 @font-face
的工作原理以及这如何影响字体加载。
@font-face
声明是使用任何 Web 字体的基本组成部分。至少,它声明了用于引用字体的名称,并指示相应字体文件的位置。
@font-face {
font-family: "Open Sans";
src: url("/fonts/OpenSans-Regular-webfont.woff2") format("woff2");
}
一个常见的误解是,当遇到 @font-face
声明时,会请求字体。这是错误的。就其本身而言,@font-face
声明不会触发字体下载。相反,只有当页面上使用的样式引用字体时,才会下载字体。例如
@font-face {
font-family: "Open Sans";
src: url("/fonts/OpenSans-Regular-webfont.woff2") format("woff2");
}
h1 {
font-family: "Open Sans"
}
在此示例中,只有当页面包含 <h1>
元素时,才会下载 Open Sans
。
因此,在考虑字体优化时,务必像考虑字体文件本身一样考虑样式表。更改样式表的内容或交付方式可能会对字体到达时间产生重大影响。同样,删除未使用的 CSS 和拆分样式表可以减少页面加载的字体数量。
内联字体声明
大多数站点会从在主文档的 <head>
中内联字体声明和其他关键样式中受益匪浅,而不是将它们包含在外部样式表中。这允许浏览器更早地发现字体声明,因为浏览器无需等待外部样式表下载。
<head>
<style>
@font-face {
font-family: "Open Sans";
src: url("/fonts/OpenSans-Regular-webfont.woff2") format("woff2");
}
body {
font-family: "Open Sans";
}
...etc.
</style>
</head>
内联关键 CSS 可能是一种更高级的技术,并非所有站点都能实现。性能优势是显而易见的,但这需要额外的流程和构建工具来确保必要的 CSS,理想情况下只有关键 CSS,被正确内联,并且任何额外的 CSS 都以非渲染阻塞的方式交付。
预连接到关键第三方来源
如果您的站点从第三方站点加载字体,强烈建议您使用 preconnect
资源提示,以建立与第三方来源的早期连接。资源提示应放置在文档的 <head>
中。以下资源提示设置了用于加载字体样式表的连接。
<head>
<link rel="preconnect" href="https://fonts.com">
</head>
要预连接用于下载字体文件的连接,请添加单独的 preconnect
资源提示,该提示使用 crossorigin
属性。与样式表不同,字体文件必须通过 CORS 连接 发送。
<head>
<link rel="preconnect" href="https://fonts.com">
<link rel="preconnect" href="https://fonts.com" crossorigin>
</head>
使用 preconnect
资源提示时,请记住字体提供商可能会从不同的来源提供样式表和字体。例如,以下是如何将 preconnect
资源提示用于 Google Fonts 的方法。
<head>
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
</head>
使用 preload
加载字体时要谨慎
虽然 preload
在使字体在页面加载过程中尽早被发现方面非常有效,但这会以占用浏览器资源来加载其他资源为代价。
内联字体声明和调整样式表可能是一种更有效的方法。这些调整更接近于解决字体延迟发现的根本原因,而不仅仅是提供一种解决方法。
此外,使用 preload
作为字体加载策略也应谨慎使用,因为它绕过了浏览器的一些内置内容协商策略。例如,preload
忽略 unicode-range
声明,如果谨慎使用,则应仅用于加载单一字体格式。
但是,当使用外部样式表时,预加载最重要的字体可能非常有效,因为否则浏览器要到稍后才会发现是否需要该字体。
字体交付
更快的字体交付产生更快的文本渲染。此外,如果字体交付得足够早,这可以帮助消除字体交换导致的布局偏移。
使用自托管字体
从理论上讲,使用自托管字体应该提供更好的性能,因为它消除了第三方连接设置。实际上,这两种选项之间的性能差异不太明显。例如,Web Almanac 发现,使用第三方字体的网站比使用第一方字体的网站具有更快的渲染速度。
如果您正在考虑使用自托管字体,请确认您的站点使用 内容交付网络 (CDN) 和 HTTP/2。如果不使用这些技术,自托管字体提供更好性能的可能性要小得多。
如果您使用自托管字体,建议您还应用一些第三方字体提供商通常自动提供的字体文件优化。例如,字体子集化和 WOFF2 压缩。应用这些优化所需的工作量在某种程度上取决于您的站点支持的语言。特别是,请注意优化 CJK 语言 的字体可能特别具有挑战性。
使用 WOFF2
在现代字体格式中,WOFF2 是最新的,具有最广泛的浏览器支持,并提供最佳压缩。由于它使用 Brotli,WOFF2 的压缩效果比 WOFF 好 30%,从而减少了要下载的数据,因此性能更快。
鉴于浏览器支持,专家现在建议仅使用 WOFF2
事实上,我们认为现在也是时候宣布:仅使用 WOFF2,忘记其他一切。
Bram Stein,来自 2022 年 Web Almanac
这将大大简化您的 CSS 和工作流程,还可以防止任何意外的双重或不正确的字体下载。WOFF2 现在在任何地方都受支持。因此,除非您需要支持真正古老的浏览器,否则只需使用 WOFF2。如果您不能这样做,请考虑根本不为这些旧浏览器提供任何 Web 字体。如果您有强大的后备策略,这不会成为问题。旧浏览器上的访问者将看到您的后备字体。
子集字体
字体文件通常包含大量 字形,用于它们支持的各种字符。但您可能不需要页面上的所有字符,并且可以通过子集字体来减小字体文件的大小。
@font-face
声明中的 unicode-range
描述符通知浏览器字体可以用于哪些字符。
@font-face {
font-family: "Open Sans";
src: url("/fonts/OpenSans-Regular-webfont.woff2") format("woff2");
unicode-range: U+0025-00FF;
}
如果页面包含一个或多个与 unicode 范围匹配的字符,则会下载字体文件。unicode-range
通常用于根据页面内容使用的语言提供不同的字体文件。
unicode-range
通常与子集化的技术结合使用。子集字体包含原始字体文件中包含的字形的一小部分。例如,站点可能会为拉丁字符和西里尔字符生成单独的子集字体,而不是向所有用户提供所有字符。
每个字体的字形数量差异很大
- 拉丁字体通常每个字体大约有 100 到 1000 个字形。
- CJK 字体可能包含超过 10,000 个字符。
删除未使用的字形可以显着减小字体的文件大小。
某些字体提供商可能会自动提供具有不同子集的不同版本的字体文件。例如,Google Fonts 默认情况下会这样做
/* devanagari */
@font-face {
font-family: 'Poppins';
font-style: normal;
font-weight: 400;
font-display: swap;
src: url(https://fonts.gstatic.com/s/poppins/v20/pxiEyp8kv8JHgFVrJJbecnFHGPezSQ.woff2) format('woff2');
unicode-range: U+0900-097F, U+1CD0-1CF6, U+1CF8-1CF9, U+200C-200D, U+20A8, U+20B9, U+25CC, U+A830-A839, U+A8E0-A8FB;
}
/* latin-ext */
@font-face {
font-family: 'Poppins';
font-style: normal;
font-weight: 400;
font-display: swap;
src: url(https://fonts.gstatic.com/s/poppins/v20/pxiEyp8kv8JHgFVrJJnecnFHGPezSQ.woff2) format('woff2');
unicode-range: U+0100-024F, U+0259, U+1E00-1EFF, U+2020, U+20A0-20AB, U+20AD-20CF, U+2113, U+2C60-2C7F, U+A720-A7FF;
}
/* latin */
@font-face {
font-family: 'Poppins';
font-style: normal;
font-weight: 400;
font-display: swap;
src: url(https://fonts.gstatic.com/s/poppins/v20/pxiEyp8kv8JHgFVrJJfecnFHGPc.woff2) format('woff2');
unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
当迁移到自托管时,这种优化可能会被忽略,并导致本地字体文件更大。
如果您的字体提供商允许,您可以手动子集字体,可以通过 API (Google Fonts 通过提供 text
参数 支持此功能),或者通过手动编辑字体文件,然后进行自托管。用于生成字体子集的工具包括 subfont 和 glyphanger。
始终检查 字体许可证以确认它们允许子集化 和自托管。
使用更少的 Web 字体
交付速度最快的字体是首先未请求的字体。系统字体和可变字体是可能减少站点上使用的 Web 字体数量的两种方法。
系统字体是用户设备的用户界面使用的默认字体。系统字体通常因操作系统和版本而异。由于字体已安装,因此无需下载字体。系统字体特别适用于正文文本。
要在 CSS 中使用系统字体,请将 system-ui
列为 font-family
font-family: system-ui
可变字体 背后的想法是,单个可变字体可以用作多个字体文件的替代品。可变字体的工作原理是定义“默认”字体样式并为操作字体提供 “轴”。例如,具有 Weight
轴的可变字体可用于实现以前需要为 light、regular、bold 和 extra bold 单独字体的排版。
并非每个人都能从切换到可变字体中受益。可变字体 包含许多样式,因此通常比仅包含一种样式的单个非可变字体具有更大的文件大小。将从使用可变字体中看到最大改进的站点是那些使用(并且需要使用)各种字体样式和粗细的站点。
字体渲染
当面临尚未加载的 Web 字体时,浏览器面临一个难题:它应该推迟渲染文本,直到 Web 字体到达吗?还是应该在后备字体中渲染文本,直到 Web 字体到达?
不同的浏览器以不同的方式处理这种情况。默认情况下,如果关联的 Web 字体尚未加载,基于 Chromium 的浏览器和 Firefox 浏览器会阻止文本渲染长达 3 秒。Safari 会无限期地阻止文本渲染。
可以通过使用 font-display
属性配置此行为。此选择可能具有重大影响:font-display
有可能影响 LCP、FCP 和布局稳定性。
选择合适的 font-display
策略
font-display
通知浏览器在关联的 Web 字体尚未加载时应如何继续进行文本渲染。它是按 font-face 定义的。
@font-face {
font-family: Roboto, Sans-Serif
src: url(/fonts/roboto.woff) format('woff'),
font-display: swap;
}
font-display
有五个可能的值
值 | 阻止期 | 交换期 |
---|---|---|
Auto | 因浏览器而异 | 因浏览器而异 |
Block | 2-3 秒 | 无限 |
Swap | 0 毫秒 | 无限 |
Fallback | 100 毫秒 | 3 秒 |
Optional | 100 毫秒 | None |
- 阻止期:阻止期在浏览器请求 Web 字体时开始。在阻止期内,如果 Web 字体不可用,则字体在不可见的后备字体中渲染,因此用户看不到文本。如果在阻止期结束时字体不可用,则以回退字体渲染。
- 交换期:交换期在阻止期之后到来。如果在交换期内 Web 字体变为可用,则会“交换”进来。
font-display
策略反映了关于性能和美观之间权衡的不同观点。因此,很难推荐一种方法,因为它取决于个人偏好、Web 字体对页面和品牌的重要性,以及后期到达的字体在交换进来时可能造成的突兀感。
对于大多数站点,根据您的首要任务,以下是三种最适用的策略
性能:使用
font-display: optional
。这是最“高性能”的方法:文本渲染延迟不超过 100 毫秒,并且保证不会出现字体交换相关的布局偏移。缺点是如果 Web 字体到达较晚,则不会使用它。快速显示文本并仍然使用 Web 字体:使用
font-display: swap
,但要确保尽早交付字体,使其不会导致布局偏移。此选项的缺点是当字体到达较晚时会发生突兀的偏移。以 Web 字体显示文本:使用
font-display: block
,但要确保尽早交付字体,使其最大限度地减少文本的延迟。初始文本显示会被延迟。尽管存在此延迟,但它仍然可能导致布局偏移,因为文本实际上是以不可见的方式绘制的,因此后备字体空间用于保留空间。一旦 Web 字体加载,这可能需要不同的空间,因此会发生偏移。与font-display: swap
相比,这可能是一种不太突兀的偏移,因为文本本身不会被看到偏移。
还要记住,这两种方法可以结合使用:例如,对品牌推广和其他视觉上独特的页面元素使用 font-display: swap
。对正文文本中使用的字体使用 font-display: optional
。
图标字体
适用于传统 Web 字体的 font-display
策略对于图标字体效果不佳。图标字体的后备字体通常看起来与图标字体显着不同,并且其字符可能传达完全不同的含义。因此,图标字体更可能导致显着的布局偏移。
此外,使用后备字体可能不切实际。如果可能,请将图标字体替换为 SVG,这也有利于无障碍功能。流行的图标字体的新版本通常支持 SVG。有关切换到 SVG 的更多信息,请参阅 Font Awesome 和 Material Icons。
减少后备字体和 Web 字体之间的偏移
为了减少 CLS 影响,您可以使用 size-adjust
属性。
结论
Web 字体仍然是性能瓶颈,但我们有越来越多的选项,使我们能够优化它们,以尽可能减少此瓶颈。