缩减网页字体大小

排版是实现良好的设计、品牌推广、可读性和无障碍性的基础。网页字体可实现上述所有目标以及其他目标:文本可选择、可搜索、可缩放且支持高 DPI,无论屏幕尺寸和分辨率如何,都能提供一致且清晰的文本呈现效果。WebFonts 对良好的设计、用户体验和性能至关重要。

网页字体优化是整体性能策略的关键组成部分。每种字体都是一种额外的资源,有些字体可能会阻止文本的渲染,但网页使用了 WebFonts 并不意味着它必须更慢地渲染。相反,如果对字体进行优化,再通过制定明智的策略来确定字体在网页上的加载和应用方式,就可以帮助减小网页总大小,并缩短网页渲染时间。

网页字体详解

网页字体是一系列字形,每个字形是描述字母或符号的矢量形状。因此,特定字体文件的大小由两个简单变量决定:每个字形矢量路径的复杂程度和特定字体中字形的数量。例如,Open Sans 是最受欢迎的 WebFonts 之一,包含 897 个字形,其中包括拉丁语、希腊语和西里尔字符。

字体字形表

选择字体时,请务必考虑支持的字符集。如果您需要将网页内容本地化为多种语言,则应使用一种能够为用户提供一致的外观和体验的字体。例如,Google 的 Noto 字体系列旨在支持全球所有语言。但请注意,Noto(含所有语言)总大小的 ZIP 文件下载大小超过了 1.1GB。

在这篇博文中,您将了解如何缩减网络字体的传输文件大小。

网页字体格式

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

WOFFWOFF 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');
}

首先,请注意,上面的示例定义了一个包含两个样式(normal 和 italic)的 Awesome Font 系列,其中的每个样式均指向一组不同的字体资源。而每个 src 描述符则包含一个以英文逗号分隔的优先资源变体列表:

  • local() 指令用于引用、加载和使用安装在本地的字体。如果用户已在系统中安装了该字体,此操作会完全绕过网络,是最快的。
  • url() 指令用于加载外部字体,可以包含可选的 format() 提示,用于指示提供的网址引用的字体格式。

当浏览器确定需要字体时,它会按指定顺序遍历提供的资源列表,并尝试加载适当的资源。以上面的示例为例:

  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 range 子集,以及为字体的每个样式变体使用单独的文件,您可以定义下载更快、更高效的复合字体系列。访问者只会下载他们需要的变体和子集,而不会强制他们下载可能永远不会在网页上看到或使用的子集。

几乎所有浏览器都支持 unicode-range。为了与旧版浏览器兼容,您可能需要回退到“手动子集内嵌”。在这种情况下,您必须回退以提供包含所有必要子集的单个字体资源,并向浏览器隐藏其余子集。例如,如果网页仅使用拉丁字符,那么您可以去除其他字形,并将该特定子集作为独立资源提供。

  1. 确定需要哪些子集
    • 如果浏览器支持 unicode-range 子集内嵌,则会自动选择正确的子集。该网页只需提供子集文件,并在 @font-face 规则中指定适当的 unicode-range。
    • 如果浏览器不支持 unicode-range 子集内嵌,则网页需要隐藏所有不必要的子集;也就是说,开发者必须指定所需的子集。
  2. 生成字体子集
    • 使用开源 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-range 以仅提供特定网页所需的字形。这样可以缩减文件大小,提高资源的下载速度。不过,在定义子集时要注意针对字体重复使用进行优化。例如,不要在每个网页上下载不同但重叠的字符集。最好根据文字系统(例如拉丁字母和西里尔字母)进行子集内嵌。
  • 优先考虑 src 列表中的 local():在 src 列表中先列出 local('Font Name') 可确保不会针对已安装的字体发出 HTTP 请求。
  • 使用 Lighthouse 测试文本压缩

对 Largest Contentful Paint (LCP) 和 Cumulative Layout Shift (CLS) 的影响

根据网页的内容,文本节点可被视为 Largest Contentful Paint (LCP) 的候选对象。因此,请务必遵循本文中的建议,确保您的网页字体尽可能小,以便用户尽快看到网页上的文字。

如果您担心,尽管网页字体资源较大,网页文字仍可能需要很长时间才能显示,不过您担心,font-display 属性提供的多项设置可帮助您在下载字体时避免不可见文本。不过,使用 swap 值可能会导致明显的布局偏移,从而影响网站的累积布局偏移 (CLS)。如果可能,请考虑使用 optionalfallback 值。

如果您的网页字体对于您的品牌(以及用户体验)至关重要,请考虑预加载字体,以便浏览器在请求字体时抢占先机。如果您使用 font-display: swap,这可以缩短交换期;如果不使用 font-display,则可以缩短屏蔽期。