缩减网页字体大小

排版对于良好的设计、品牌推广、可读性和无障碍性至关重要。Web 字体可实现上述所有功能,甚至更多:文本可选择、可搜索、可缩放,并且支持高 DPI,无论屏幕大小和分辨率如何,都能提供一致且清晰的文本呈现效果。Web 字体对于良好的设计、用户体验和性能至关重要。

网页字体优化是整体性能策略的关键组成部分。每种字体都是一种额外的资源,有些字体可能会阻止文本的呈现,但网页使用 WebFont 并不一定意味着呈现速度会变慢。相反,优化后的字体与明智的加载和应用策略相结合,有助于减小网页总大小并缩短网页渲染时间。

网络字体剖析

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

字体字形表

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

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

网络字体格式

目前,网页上使用了两种推荐的字体容器格式:

WOFFWOFF 2.0 得到广泛支持,并且所有现代浏览器都支持这两种格式。

  • 向现代浏览器提供 WOFF 2.0 变体。
  • 如果绝对必要(例如,您仍需要支持 Internet Explorer 11),请将 WOFF 作为后备格式提供。
  • 或者,您也可以考虑不为旧版浏览器使用 Web 字体,而是回退到系统字体。对于较旧且受限较多的设备,这可能也会带来更好的性能。
  • 由于 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 @ 规则,您可以定义特定字体资源的位置、其样式特征以及应使用该字体的 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() 提示,用于指明所提供网址引用的字体的格式。

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

  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 Fonts)默认会自动进行子集化。
    • 某些字体服务允许通过自定义查询参数进行手动子集化,您可以使用这些参数手动指定网页所需的子集。请参阅字体提供商提供的文档。

字体选择和合成

每个字体系列可能包含多种样式变体(常规、粗体、斜体),每种样式又包含多种粗细。而每个字形变体又可能包含截然不同的字形,例如不同的间距、大小或完全不同的形状。

图片:显示字体粗细逐渐增加的字母 A。

上图展示了一个提供三种不同粗体字重的字体系列:

  • 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,会发生什么情况?

  • 如果没有完全匹配的字体,浏览器会替换为最接近的字体。
  • 如果未找到样式匹配项(例如,在上面的示例中未声明任何斜体变体),则浏览器会合成自己的字体变体。
字体合成,显示了从粗细 300 到 800 的“lorem ipsum”语句,其中斜体条目交替出现。

上例展示了 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 字体尽可能小,以便用户尽快看到您网页上的文字。

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

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