缩减网页字体大小

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

网页字体优化是总体性能策略中至关重要的一环。每种字体都是一项额外的资源,并且某些字体可能会阻止文本的渲染,但网页使用 WebFonts 并不意味着它的渲染速度必须减慢。相反,如果对字体进行优化,再结合在网页上加载和应用这些字体的明智策略,就可以帮助减小网页总大小并缩短网页呈现时间。

网页字体详解

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

字体字形表

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

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

网页字体格式

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

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

几乎所有浏览器都支持 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,会发生什么情况?

  • 如果没有完全匹配的字体,浏览器会替换最接近的匹配项。
  • 如果未找到样式匹配项(例如,上例中未声明任何斜体变体),则浏览器会合成自己的字体变体。
。 <ph type="x-smartling-placeholder">
</ph> 字体合成

上例说明了 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 值可能会导致明显的布局偏移,进而影响您网站的 Cumulative Layout Shift (CLS)。如果可能,请考虑使用 optionalfallback 值。

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