字体排版对于良好的设计、品牌宣传、可读性和无障碍功能至关重要。网络字体不仅能实现上述所有功能,还能提供更多功能:文本可选择、可搜索、可缩放,并且与高 DPI 兼容,无论屏幕大小和分辨率如何,都能提供一致且清晰的文本呈现效果。网络字体对于良好的设计、用户体验和性能至关重要。
网络字体优化是整体性能策略的关键组成部分。每种字体都是额外的资源,有些字体可能会阻止文本呈现,但页面使用网络字体并不意味着呈现速度一定会变慢。相反,经过优化的字体,再加上明智的加载和应用策略,有助于减小页面总大小并缩短页面呈现时间。
网络字体剖析
网络字体是一组字形,每个字形都是描述字母或符号的矢量形状。因此,有两个简单的变量决定了特定字体文件的大小:每个字形的矢量路径的复杂程度,以及特定字体中的字形数量。例如,Open Sans 是最受欢迎的网络字体之一,包含 897 个字形,其中包括拉丁字符、希腊字符和西里尔字符。
选择字体时,务必考虑支持哪些字符集。如果您需要将网页内容本地化为多种语言,则应使用能够为用户提供一致外观和体验的字体。例如,Google 的 Noto 字体系列旨在支持世界上所有语言。不过请注意,如果包含所有语言,Noto 的总大小将超过 1.1 GB,需要下载 ZIP 文件。
在本文中,您将了解如何减小网络字体的传送文件大小。
网络字体格式
目前,网络上使用了两种推荐的字体容器格式:
WOFF 和 WOFF 2.0 获得了广泛支持,并且所有现代浏览器都支持这两种格式。
- 向现代浏览器提供 WOFF 2.0 变体。
- 如果绝对必要(例如,如果您仍然需要支持 Internet Explorer 11),请将 WOFF 作为回退方案。
- 或者,考虑不要为旧版浏览器使用网络字体,而是回退到系统字体。对于较旧、资源受限的设备,这可能也会带来更好的性能。
- 由于 WOFF 和 WOFF 2.0 涵盖了仍在使用的现代浏览器和旧版浏览器的所有基础,因此不再需要使用 EOT 和 TTF,并且使用这两种格式可能会导致网络字体下载时间变长。
网络字体和压缩
WOFF 和 WOFF 2.0 都具有内置压缩功能。WOFF 2.0 的内部压缩使用 Brotli,与 WOFF 相比,压缩率最高可提高 30%。如需了解详情,请参阅 WOFF 2.0 评估报告。
最后,值得注意的是,某些字体格式包含额外的元数据,例如 字体提示 和 字距调整 信息,这些信息在某些平台上可能并不必要,因此可以进一步优化文件大小。例如,Google Fonts 为每种字体维护 30 多个经过优化的变体,并自动检测和提供适用于每个平台和浏览器的最佳变体。
使用 @font-face 定义字体系列
借助 @font-face CSS at-rule,您可以定义特定字体资源的位置、样式特征以及应使用的 Unicode 代码点。您可以结合使用此类 @font-face 声明来构建“字体系列”,浏览器将使用该字体系列来评估需要下载哪些字体资源并将其应用于当前页面。
考虑使用可变字体
如果您需要字体的多个变体,可变字体可以显著减小字体的文件大小。您无需加载常规样式和粗体样式及其斜体版本,只需加载包含所有信息的一个文件即可。不过,可变字体文件的大小将大于单个字体变体,但小于多个变体的组合。与其使用一个大型可变字体,不如先提供关键字体变体,稍后再下载其他变体。
现在,所有现代浏览器都支持可变字体,如需了解详情,请参阅网络上的可变字体简介。
选择正确的格式
每个 @font-face 声明都提供字体系列的名称(充当多个声明的逻辑组)、字体属性(例如样式、粗细和拉伸)以及 src 描述符(用于指定字体资源的优先级列表)。
@font-face {
font-family: 'Awesome Font';
font-style: normal;
font-weight: 400;
src: local('Awesome Font'),
url('/fonts/awesome.woff2') format('woff2'),
/* Only serve WOFF if necessary. Otherwise,
WOFF 2.0 is fine by itself. */
url('/fonts/awesome.woff') format('woff');
}
@font-face {
font-family: 'Awesome Font';
font-style: italic;
font-weight: 400;
src: local('Awesome Font Italic'),
url('/fonts/awesome-i.woff2') format('woff2'),
url('/fonts/awesome-i.woff') format('woff');
}
首先,请注意,上述示例定义了一个 Awesome Font 系列,其中包含两种样式(普通和 斜体),每种样式都指向一组不同的字体资源。反过来,每个 src 描述符都包含一个资源变体的优先级逗号分隔列表:
- 借助
local()指令,您可以引用、加载和使用本地安装的字体。如果用户已在其系统上安装了该字体,则此指令会完全绕过网络,速度最快。 - 借助
url()指令,您可以加载外部字体,并且可以包含可选的format()提示,用于指明所提供网址引用的字体的格式。
当浏览器确定需要该字体时,它会按指定顺序遍历提供的资源列表,并尝试加载相应的资源。例如,按照上面的示例:
- 浏览器执行页面布局,并确定呈现页面上指定的文本所需的字体变体。浏览器不会下载不属于页面的 CSS 对象模型 (CSSOM) 的字体,因为这些字体不是必需的。
- 对于每种必需的字体,浏览器都会检查该字体是否在本地可用。
- 如果该字体在本地不可用,浏览器会遍历外部定义:
- 如果存在格式提示,浏览器会在启动下载之前检查是否支持该提示。如果浏览器不支持该提示,则会继续检查下一个提示。
- 如果不存在格式提示,浏览器会下载该资源。
通过将本地指令和外部指令与适当的格式提示相结合,您可以指定所有可用的字体格式,并让浏览器处理其余部分。浏览器会确定所需的资源并选择最佳格式。
Unicode 范围子集
除了样式、粗细和拉伸等字体属性之外,借助 @font-face 规则,您还可以定义每个资源支持的一组 Unicode 代码点。这样,您就可以将大型 Unicode 字体拆分为较小的子集(例如,拉丁子集、西里尔子集和希腊子集),并且仅下载呈现特定页面上的文本所需的字形。
借助 unicode-range 描述符,您可以指定以英文逗号分隔的范围值列表,每个值都可以采用以下三种不同形式之一:
- 单个代码点(例如
U+416) - 间隔范围(例如
U+400-4ff):表示范围的起始和结束代码点 - 通配符范围(例如
U+4??):?字符表示任何十六进制数字
例如,您可以将 Awesome Font 系列拆分为拉丁子集和日文子集,浏览器会根据需要下载每个子集:
@font-face {
font-family: 'Awesome Font';
font-style: normal;
font-weight: 400;
src: local('Awesome Font'),
url('/fonts/awesome-l.woff2') format('woff2');
/* Latin glyphs */
unicode-range: U+000-5FF;
}
@font-face {
font-family: 'Awesome Font';
font-style: normal;
font-weight: 400;
src: local('Awesome Font'),
url('/fonts/awesome-jp.woff2') format('woff2');
/* Japanese glyphs */
unicode-range: U+3000-9FFF, U+ff??;
}
通过使用 Unicode 范围子集和单独的文件来表示字体的每种样式变体,您可以定义一个复合字体系列,该系列不仅下载速度更快,而且效率更高。访问者只需下载所需的变体和子集,而无需下载他们可能永远不会在页面上看到或使用的子集。
几乎所有浏览器都 支持 unicode-range。为了与旧版浏览器兼容,您可能需要回退到“手动子集”。在这种情况下,您必须回退到提供包含所有必要子集的单个字体资源,并向浏览器隐藏其余部分。例如,如果页面仅使用拉丁字符,则可以剥离其他字形并将该特定子集作为独立资源提供。
- 确定需要哪些子集:
- 如果浏览器支持 Unicode 范围子集,则会自动选择正确的子集。页面只需提供子集文件并在
@font-face规则中指定适当的 Unicode 范围即可。 - 如果浏览器不支持 Unicode 范围子集,则页面需要隐藏所有不必要的子集;也就是说,开发者必须指定所需的子集。
- 如果浏览器支持 Unicode 范围子集,则会自动选择正确的子集。页面只需提供子集文件并在
- 生成字体子集:
- 使用开源 pyftsubset 工具 对字体进行子集化和优化。
- 某些字体服务器(例如 Google Font)默认会自动进行子集化。
- 某些字体服务允许通过自定义查询参数进行手动子集化,您可以使用这些参数为页面手动指定所需的子集。请参阅字体提供商提供的文档。
字体选择和合成
每个字体系列可能由多个样式变体(普通、粗体、斜体)和每种样式的多个粗细组成。反过来,每个变体可能包含非常不同的字形形状,例如不同的间距、大小或完全不同的形状。
上图展示了一个提供三种不同粗细的粗体字体的字体系列:
- 400(普通)。
- 700(粗体)。
- 900(特粗)。
所有其他中间变体(以灰色表示)都会由浏览器自动映射到最接近的变体。
如果指定的粗细不存在,则会使用粗细相近的字体。一般来说,粗体粗细会映射到粗细较重的字体,而细体粗细会映射到粗细较轻的字体。
CSS 字体匹配算法
类似的逻辑也适用于 斜体变体。字体设计者控制他们将生成哪些变体,而您控制将在页面上使用哪些变体。由于每个变体都是单独下载的,因此最好尽量减少变体的数量。例如,您可以为 Awesome Font 系列定义两个粗体变体:
@font-face {
font-family: 'Awesome Font';
font-style: normal;
font-weight: 400;
src: local('Awesome Font'),
url('/fonts/awesome-l.woff2') format('woff2');
/* Latin glyphs */
unicode-range: U+000-5FF;
}
@font-face {
font-family: 'Awesome Font';
font-style: normal;
font-weight: 700;
src: local('Awesome Font'),
url('/fonts/awesome-l-700.woff2') format('woff2');
/* Latin glyphs */
unicode-range: U+000-5FF;
}
上面的示例声明了 Awesome Font 系列,该系列由两个资源组成,这两个资源涵盖同一组拉丁字形 (U+000-5FF),但提供两种不同的“粗细”:普通 (400) 和粗体 (700)。不过,如果您的某个 CSS 规则指定了不同的字体粗细,或者将 font-style 属性设置为 italic,会发生什么情况?
- 如果没有完全匹配的字体,浏览器会替换为最接近的匹配项。
- 如果找不到样式匹配项(例如,在上面的示例中未声明任何斜体变体),则浏览器会合成自己的字体变体。
上面的示例展示了 Open Sans 的实际字体结果与合成字体结果之间的区别。所有合成变体都是从单个粗细为 400 的字体生成的。如您所见,结果存在明显的差异。如何生成粗体变体和倾斜变体的详细信息未指定。因此,结果因浏览器而异,并且高度依赖于字体。
网络字体大小优化核对清单
- 审核和监控字体使用情况 :不要在页面上使用过多字体,并且对于每种字体,尽量减少使用的变体数量。这有助于为用户提供更一致、更快速的体验。
- 尽可能避免使用旧格式 :EOT、TTF 和 WOFF 格式比 WOFF 2.0 大。EOT 和 TTF 是完全不必要的格式,而如果您需要支持 Internet Explorer 11,则可以使用 WOFF。如果您仅以现代浏览器为目标,则仅使用 WOFF 2.0 是最简单且性能最佳的选项。
- 对字体资源进行子集化 :许多字体都可以进行子集化,或拆分为多个 Unicode 范围,以便仅提供特定页面所需的字形。这可以减小文件大小并提高资源的下载速度。不过,在定义子集时,请注意针对字体重用进行优化。例如,不要在每个页面上下载一组不同但重叠的字符。一种好的做法是根据脚本进行子集化:例如,拉丁文和西里尔文。
- 在
src列表中优先使用local(): 在src列表中首先列出local('Font Name')可确保不会为已安装的字体发出 HTTP 请求。 - 使用 Lighthouse 测试 文本压缩。
对 Largest Contentful Paint (LCP) 和 Cumulative Layout Shift (CLS) 的影响
根据页面的内容,文本节点可以被视为 Largest Contentful Paint (LCP) 的候选对象。因此,务必按照本文中的建议确保网络字体尽可能小,以便用户能够尽快看到页面上的文本 as soon as they possibly can。
如果您担心,尽管您做出了优化努力,但由于网络字体资源较大,页面文本可能需要很长时间才能显示,那么 font-display 属性提供了许多设置,可帮助您 避免在下载字体时显示不可见的文本。不过,使用 swap 值可能会导致布局发生重大变化,从而影响网站的 Cumulative Layout Shift (CLS)。请尽可能考虑使用 optional 或 fallback 值。
如果网络字体对于您的品牌宣传(以及用户体验)至关重要,请考虑预加载字体,以便浏览器能够抢先请求这些字体。如果您使用 font-display: swap,这可以缩短交换期;如果您不使用 font-display,则可以缩短阻塞期。