Web Performance

在大多数网站上,Web 字体仍然是最容易获得的性能收益

距离 variable fonts 推出已有五年,距离 WOFF2 普及已有十年,但普通网站加载字体的方式仍然不对。下面是把它做对的简明版本。

The Wux Webtools Team The Wux Webtools Team 4 分钟阅读
Editorial illustration of a single bold letter glyph with subtle weight axis lines, soft green palette
目录
  1. 反复出现的模式
  2. 用一个 variable font 代替六个静态字体
  3. 设置合理的 `font-display`
  4. 匹配 fallback 指标
  5. 自托管
  6. 什么时候完全跳过自定义字体
  7. 坦率总结

反复出现的模式

如果你今天随机审计十个生产网站,会发现其中大多数的字体状况大致相同:

  • 首页加载了六到十个字体文件
  • 它们全都是 WOFF2(好),但没有 font-display 策略(不好)
  • 有几个字重和样式在页面任何地方都没有用到
  • 整套字体栈都由第三方域名提供(通常是 Google Fonts),随之带来 DNS、TLS 和隐私成本
  • 没有 unicode-range 子集化,因此即使页面是英文,每位访问者也会下载西里尔和希腊字形

这并不是小网站独有的问题。许多资金充足的营销页面,仍然会在第一段文字可绘制之前加载 600 KB 的字体。一旦你开始注意到这种模式,就很难再忽视它。

修复方法并不稀奇。它只是一些浏览器多年来一直支持、已经被充分理解的技术。

用一个 variable font 代替六个静态字体

如果你正在加载 Inter Regular、Inter Medium、Inter SemiBold、Inter Bold 以及 它们的斜体,那么你下载的字节数大约是实际需要的六倍。一个 Inter variable font 可以用一个文件覆盖整个字重轴(在某些构建中也包括倾斜轴),而这个文件只比两个静态字重略大。

浏览器支持的问题多年前就已经解决了——variable fonts 在所有重要的浏览器中都可用。剩下的迟疑,大多只是静态字体时代留下的肌肉记忆。

一个实用的经验法则:每种文字、每个字体族使用一个 variable font 文件。拉丁文字一个文件,西里尔文字另一个,希腊文字第三个,并用 unicode-range 条件加载。就这么简单。

设置合理的 font-display

当自定义字体仍在加载时,默认行为是显示什么都没有——也就是不可见文本——最长可达三秒。这是最糟糕的默认值。用户看到空白页面,会以为哪里坏了。

把下面这段加到每个 @font-face 规则中:

@font-face {
  font-family: 'Inter';
  src: url('/fonts/inter-var.woff2') format('woff2-variations');
  font-weight: 100 900;
  font-display: swap;
}

swap 会立即显示 fallback 字体,并在自定义字体加载完成后替换进去。用户从第零毫秒就能阅读页面。代价是替换发生时会有一次短暂的布局偏移,而你可以用下一项技术来缓解它。

匹配 fallback 指标

只有当 fallback 字体和自定义字体的指标差异很大时,无样式文本闪烁才会显得刺眼。现代 CSS 用 size-adjust、ascent-override 以及相关属性解决了这个问题:

@font-face {
  font-family: 'Inter Fallback';
  src: local('Arial');
  size-adjust: 107%;
  ascent-override: 90%;
  descent-override: 22%;
  line-gap-override: 0%;
}

调整好 fallback 指标后,替换几乎难以察觉——自定义字体到达前后,文字占据的水平空间保持一致。

Google 的 --allow-fallback-font-metrics 工作现在已经成为浏览器基线支持,并且已有工具链(fontaine 库及类似工具)可以在几秒内为你计算出正确数值。

自托管

Google Fonts 很方便、免费,但也带来两个真实成本:

  1. 第二次 TLS 握手。 即使有 HTTP/3 和连接合并,额外的 origin 也很少是免费的。
  2. 隐私和合规问题。 多个欧洲法院已经裁定,从 fonts.gstatic.com 加载 Google Fonts 构成向第三方传输个人数据(IP 地址)。自托管可以彻底消除这个问题。

下载并托管的流程是:

  1. 获取 WOFF2 文件(如果存在 variable build,就用它)
  2. 将它子集化到你的受众实际使用的文字
  3. 从与网站其余部分相同的 origin 提供它,并设置较长的 Cache-Control: max-age=31536000, immutable
  4. 预加载最关键的字重:<link rel="preload" as="font" type="font/woff2" href="/fonts/inter-var.woff2" crossorigin>

这就是完整流程。对现有网站来说,它需要一个下午,而且你几乎不会再回头。

什么时候完全跳过自定义字体

有必要直接说清楚:并不是每个网站都需要自定义字体。系统字体栈现在在每个操作系统上都确实很漂亮:

font-family: system-ui, -apple-system, 'Segoe UI', Roboto, Inter, sans-serif;

字重是对的,指标是对的,渲染针对设备做了调优,线上传输的字节成本恰好是零。对于工具类网站、内部应用、优先考虑内容的作品集,或任何面向慢速网络用户的产品,系统字体栈就是正确答案。

<!-- tool-cta:start -->

💡 试试这个: 使用 Webfont Generator 将你的 TTF 或 OTF 文件转换为带有即用型 CSS 的现代 WOFF2,这正是文章推荐的格式转换。

<!-- tool-cta:end -->

坦率总结

对于一个典型的小型网站,四个步骤就覆盖了整个 Web 字体性能问题:

  1. 每个字体族、每种文字使用一个 variable font
  2. 使用 font-display: swap,并配合匹配的 fallback 指标
  3. 自托管,设置较长的缓存头,并为关键字重添加 preload
  4. 或者完全跳过字体,使用系统字体栈

做一次这些工作,你就能从首页收回数百 KB,获得几百毫秒的感知性能提升,并顺手移除一个第三方依赖。很少有改动能有这么高的投入产出比。

Four-step checklist for faster web fonts: variable font, font-display swap with matched fallback metrics, self-host with cache and preload, or use system stack
InfographicThe 4-step web font performance fix — A compact playbook for reclaiming several hundred kilobytes and improving perceived performance
Comparison of six static font files versus one variable font file with script-based subsetting for Latin, Cyrillic, and Greek
InfographicFrom six static font files to one variable font — One variable file can replace a pile of weights and italics with far fewer bytes
Four-step self-hosted font pipeline: get WOFF2 variable file, subset by script, serve from same origin with immutable cache, preload critical weight
InfographicSelf-hosting web fonts: the simple delivery pipeline — A same-origin font pipeline cuts extra origin cost and removes the privacy question

常见问题

Variable fonts 到处都支持了吗?
是的——所有重要浏览器多年前就已经支持 variable font。你有时听到的迟疑,是静态字体时代留下的肌肉记忆;在 2026 年,这并不是真正的兼容性问题。
我应该手动对子集化字体吗?
对于仅使用拉丁文字的网站,是的——拉丁子集的大小大约是完整多语言文件的一半。对于多语言网站,使用 `unicode-range` 将每种文字作为单独文件加载,这样访问者只会下载其内容实际需要的部分。
为什么 font-display: swap 有时会造成难看的闪烁?
因为 fallback 字体的指标与自定义字体不同,所以替换发生时文字会重新排版。在 fallback 上使用 `size-adjust`、`ascent-override` 和 `descent-override` 来匹配自定义字体的指标——闪烁会变得几乎不可见。
Google Fonts 真的是隐私问题吗?
多项欧洲法院判决认为,从 `fonts.gstatic.com` 加载会在未经同意的情况下将访问者 IP 传输给第三方,在某些解释下这违反 GDPR。自托管可以完全消除这个问题,而且通常也更快。

来源与进一步阅读

  1. MDN — `@font-face` and `font-display`
  2. MDN — Variable fonts guide
  3. web.dev — Reduce web font size
关于作者
The Wux Webtools Team

最后更新:

继续阅读