缩小网络字体大小

排版是良好设计、品牌塑造、可读性和无障碍功能的基础。Web 字体使以上所有功能以及更多功能成为可能:文本是可选择、可搜索、可缩放的,并且对高 DPI 友好,无论屏幕尺寸和分辨率如何,都能提供一致且清晰的文本渲染。WebFont 对于良好的设计、用户体验和性能至关重要。

Web 字体优化是整体性能策略的关键部分。每种字体都是额外的资源,有些字体可能会阻止文本的渲染,但这并不意味着页面使用了 WebFont 就一定会降低渲染速度。相反,优化的字体,再加上明智的加载和应用于页面的策略,可以帮助减少页面总大小并缩短页面渲染时间。

Web 字体的剖析

Web 字体是字形的集合,每个字形都是一个矢量形状,用于描述字母或符号。因此,两个简单的变量决定了特定字体文件的大小:每个字形的矢量路径的复杂性和特定字体中字形的数量。例如,Open Sans 是最流行的 WebFont 之一,包含 897 个字形,其中包括拉丁文、希腊文和西里尔文字符。

Font glyph table

在选择字体时,务必考虑支持哪些字符集。如果您需要将页面内容本地化为多种语言,则应使用可以为用户提供一致外观和体验的字体。例如,Google 的 Noto 字体系列旨在支持世界上所有语言。但请注意,包含所有语言的 Noto 总大小会导致 1.1GB+ ZIP 下载。

在本文中,您将了解如何减少 Web 字体的交付文件大小。

Web 字体格式

如今,Web 上使用了两种推荐的字体容器格式

WOFFWOFF 2.0 享有广泛的支持,并且受所有现代浏览器支持。

  • 为现代浏览器提供 WOFF 2.0 变体。
  • 如果绝对必要(例如,如果您仍然需要支持 Internet Explorer 11),请提供 WOFF 作为后备方案。
  • 或者,考虑不要为旧版浏览器使用 Web 字体,而回退到系统字体。对于较旧、约束更多的设备,这可能更高效。
  • 由于 WOFF 和 WOFF 2.0 涵盖了仍在使用的现代浏览器和旧版浏览器的所有情况,因此不再需要使用 EOT 和 TTF,并且可能会导致 Web 字体下载时间更长。

Web 字体和压缩

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 声明的组合可用于构建“字体系列”,浏览器将使用该字体系列来评估需要下载哪些字体资源并将其应用于当前页面。

考虑可变字体

在您需要字体的多个变体的情况下,可变字体可以显着减少字体的文件大小。您无需加载常规和粗体样式及其斜体版本,而是可以加载包含所有信息的单个文件。但是,可变字体文件大小将大于单个字体变体,但小于多个变体的组合。与其使用一个大型可变字体,不如先提供关键字体变体,稍后再下载其他变体。

所有现代浏览器现在都支持可变字体,请在 Web 上可变字体简介中了解更多信息。

选择正确的格式

每个 @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 系列,其中包含两种样式(normal 和 italic),每种样式都指向一组不同的字体资源。反过来,每个 src 描述符都包含一个优先级排序的、逗号分隔的资源变体列表

  • local() 指令允许您引用、加载和使用本地安装的字体。如果用户系统上已安装该字体,则会完全绕过网络,并且速度最快。
  • url() 指令允许您加载外部字体,并且允许包含可选的 format() 提示,指示提供的 URL 引用的字体格式。

当浏览器确定需要字体时,它会按指定顺序迭代提供的资源列表,并尝试加载相应的资源。例如,按照上面的示例

  1. 浏览器执行页面布局并确定呈现页面上指定的文本所需的字体变体。浏览器不会下载不属于页面 CSS 对象模型 (CSSOM) 的字体,因为它们不是必需的。
  2. 对于每个必需的字体,浏览器都会检查该字体是否在本地可用。
  3. 如果字体在本地不可用,浏览器将迭代外部定义
    • 如果存在格式提示,浏览器会在启动下载之前检查它是否支持该提示。如果浏览器不支持该提示,则浏览器将前进到下一个提示。
    • 如果不存在格式提示,浏览器将下载资源。

本地和外部指令与适当的格式提示相结合,允许您指定所有可用的字体格式,并让浏览器处理其余部分。浏览器确定需要哪些资源并选择最佳格式。

Unicode-range 子集化

除了样式、粗细和拉伸等字体属性外,@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。为了与旧版浏览器兼容,您可能需要回退到“手动子集化”。在这种情况下,您必须回退到提供包含所有必要子集的单个字体资源,并从浏览器中隐藏其余部分。例如,如果页面仅使用拉丁字符,则可以剥离其他字形并将该特定子集作为独立资源提供。

  1. 确定需要哪些子集
    • 如果浏览器支持 unicode-range 子集化,则它将自动选择正确的子集。页面只需要提供子集文件并在 @font-face 规则中指定适当的 unicode-range。
    • 如果浏览器不支持 unicode-range 子集化,则页面需要隐藏所有不必要的子集;也就是说,开发人员必须指定所需的子集。
  2. 生成字体子集
    • 使用开源 pyftsubset 工具来子集化和优化您的字体。
    • 某些字体服务器(例如 Google Font)默认情况下会自动进行子集化。
    • 某些字体服务允许通过自定义查询参数进行手动子集化,您可以使用这些参数手动指定页面所需的子集。请查阅您的字体提供商的文档。

字体选择和合成

每个字体系列可能由多个样式变体(常规、粗体、斜体)和每个样式的多个粗细组成。反过来,每种样式可能包含非常不同的字形形状,例如,不同的间距、大小或完全不同的形状。

Font weights

上图说明了一个字体系列,该系列提供三种不同的粗体粗细

  • 400(常规)。
  • 700(粗体)。
  • 900(特粗体)。

所有其他介于两者之间的变体(以灰色指示)都由浏览器自动映射到最接近的变体。

当指定的粗细不存在面时,将使用具有附近粗细的面。一般来说,粗体粗细映射到具有较重粗细的面,而细体粗细映射到具有较轻粗细的面。

CSS 字体匹配算法

类似的逻辑适用于 italic 变体。字体设计者控制他们将生成哪些变体,而您控制将在页面上使用哪些变体。由于每个变体都是单独下载的,因此最好尽量减少变体的数量。例如,您可以为 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,会发生什么情况?

  • 如果找不到完全匹配的字体,浏览器将替换最接近的匹配项。
  • 如果找不到样式匹配项(例如,在上面的示例中未声明斜体变体),则浏览器会合成自己的字体变体。
Font synthesis

上面的示例说明了 Open Sans 的实际字体结果与合成字体结果之间的差异。所有合成变体均从单个 400 粗细字体生成。如您所见,结果存在明显的差异。如何生成粗体和倾斜变体的详细信息未指定。因此,结果因浏览器而异,并且高度依赖于字体。

Web 字体大小优化清单

  • 审核和监控您的字体使用情况: 不要在您的页面上使用过多的字体,并且对于每种字体,尽量减少使用的变体数量。这有助于为您的用户提供更一致、更快速的体验。
  • 如果可能,避免使用旧版格式: EOT、TTF 和 WOFF 格式比 WOFF 2.0 大。EOT 和 TTF 是完全不必要的格式,而如果您需要支持 Internet Explorer 11,则 WOFF 可能是可以接受的。如果您仅以现代浏览器为目标,则仅使用 WOFF 2.0 是最简单且性能最高的选择。
  • 子集化您的字体资源: 许多字体可以进行子集化,或拆分为多个 unicode-range,以便仅交付特定页面所需的字形。这可以减小文件大小并提高资源下载速度。但是,在定义子集时,请注意优化字体重用。例如,不要在每个页面上下载一组不同但重叠的字符。一个好的做法是基于脚本进行子集化:例如,拉丁文和西里尔文。
  • 在您的 src 列表中优先考虑 local() 在您的 src 列表中首先列出 local('Font Name') 可确保不会为已安装的字体发出 HTTP 请求。
  • 使用 Lighthouse 测试 文本压缩

对最大内容渲染 (LCP) 和累积布局偏移 (CLS) 的影响

根据您页面的内容,文本节点可以被视为 最大内容渲染 (LCP) 的候选对象。因此,至关重要的是,通过遵循本文中的建议来确保您的 Web 字体尽可能小,以便您的用户能够尽快看到页面上的文本

如果您担心,尽管您做出了优化努力,但由于 Web 字体资源过大,页面文本可能需要太长时间才能显示,则 font-display 属性有许多设置可以帮助您在字体下载时避免不可见文本。但是,使用 swap 值可能会导致显着的布局偏移,从而影响您网站的 累积布局偏移 (CLS)。如果可能,请考虑使用 optionalfallback 值。

如果您的 Web 字体对您的品牌(以及扩展的用户体验)至关重要,请考虑预加载您的字体,以便浏览器可以提前开始请求它们。如果您使用 font-display: swap,这可以减少交换期;如果您不使用 font-display,则可以减少阻止期。