缩减网页字体大小

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

网站字体优化是整体效果策略中至关重要的一部分。每个字体都是额外的资源,有些字体可能会阻止文本呈现,但网页使用 WebFonts 并不一定会导致呈现速度变慢。相反,优化的字体以及合理的字体加载和应用策略有助于缩减总页面大小并缩短页面呈现时间。

网站字体的构成

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

字体字形表

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

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

网页字体格式

目前,网络上有两种推荐的字体容器格式:

WOFFWOFF 2.0 得到广泛支持,所有新型浏览器都支持它们。

  • 向新型浏览器提供 WOFF 2.0 变体。
  • 如果绝对必要(例如,您仍需要支持 Internet Explorer 11),请将 WOFF 作为后备服务。
  • 或者,您也可以考虑不为旧版浏览器使用 Web 字体,而是回退到系统字体。对于配置较低的旧款设备,这种方法可能也能带来更出色的性能。
  • 由于 WOFF 和 WOFF 2.0 涵盖了仍在使用的现代浏览器和旧版浏览器的所有基础,因此不再需要使用 EOT 和 TTF,这可能会导致 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 字体系列,每种样式都指向一组不同的字体资源。反过来,每个 src 描述符都包含以逗号分隔的优先级资源变体列表:

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

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

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

通过将本地和外部指令与适当的格式提示组合使用,您可以指定所有可用的字体格式,并让浏览器处理其余部分。浏览器会确定所需的资源并选择最佳格式。

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

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

字体选择和合成

每个字体系列可以由多种样式变体(普通、粗体、斜体)组成,并且每种样式可以有多个粗细。而每种字体可能包含非常不同的字形形状,例如不同的间距、尺寸或完全不同的形状。

字体粗细

上图展示了一个字体系列,其中包含三种不同的粗体粗细:

  • 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) 的候选项。因此,请务必按照本文中的建议,确保您的 Web 字体尽可能小,以便用户尽快看到您网页上的文字。

如果您担心,尽管您进行了优化,但由于 Web 字体资源较大,网页文字可能需要很长时间才能显示,那么 font-display 属性提供了一些设置,可帮助您在字体下载期间避免文字不可见。不过,使用 swap 值可能会导致严重的布局偏移,从而影响您网站的 Cumulative Layout Shift (CLS)。请尽可能考虑使用 optionalfallback 值。

如果您的 Web 字体对品牌推广至关重要,进而对用户体验也至关重要,不妨考虑预加载字体,以便浏览器提前请求这些字体。如果您使用 font-display: swap,这可以缩短交换期;如果您不使用 font-display,则可以缩短阻塞期。