Back

现代Web字体加载策略优化性能

现代Web字体加载策略优化性能

Web字体可以成就或毁掉你网站的性能。糟糕的字体策略会导致文本不可见、布局偏移和用户体验不佳。然而大多数开发者仍然像2015年那样加载字体——传输庞大的文件,忽略现代CSS特性,只是抱着最好的希望。

本文涵盖了你今天就能实施的实用字体加载策略:利用WOFF2压缩、使用font-display控制渲染、智能字体子集化、使用系统字体栈作为回退、预加载关键字体,以及在合适的场景下采用可变字体。你将学会如何在性能与排版之间取得平衡,同时改善Core Web Vitals指标。

核心要点

  • 仅WOFF2格式就能提供30%更好的压缩率和通用浏览器支持
  • font-display属性控制字体加载期间的文本可见性
  • 字体子集化可以为单语言网站减少70%的文件大小
  • 系统字体栈提供即时回退并防止布局偏移
  • 当你需要多种字重或平滑动画时,可变字体效果最佳

选择WOFF2并淘汰旧格式

停止传输多种字体格式。WOFF2比WOFF提供30%更好的压缩率,并且拥有通用浏览器支持。除非你需要支持IE11,否则WOFF2就是你所需要的一切。

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

在WOFF2之外还提供TTF、OTF或WOFF格式会浪费带宽。每个访问者都要为几乎不存在的浏览器承担性能代价。使用FontSquirrelCloudConvert转换现有字体。

使用font-display控制渲染

font-display描述符决定浏览器如何处理字体请求和渲染之间的间隙:

@font-face {
  font-family: 'Inter';
  src: url('/fonts/inter.woff2') format('woff2');
  font-display: swap; /* 立即显示回退字体 */
}

你的选项:

  • swap:立即显示回退文本,准备好时切换(防止FOIT)
  • fallback:短暂的不可见期(约100ms),然后回退
  • optional:仅在立即可用时使用字体
  • block:隐藏文本长达3秒(避免使用)

对于正文文本,使用swap。对于装饰性字体,考虑optional。这一行代码就能消除文本不可见的问题。

实施智能字体子集化

大多数网站传输的是完整字符集,但它们永远不会使用。一个完整的拉丁扩展字体可能超过100KB,而你的英文网站可能只需要30KB的字形。

使用unicode-range按脚本分割字体:

/* 拉丁文 */
@font-face {
  font-family: 'Inter';
  src: url('/fonts/inter-latin.woff2') format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153;
}

/* 拉丁扩展 */
@font-face {
  font-family: 'Inter';
  src: url('/fonts/inter-latin-ext.woff2') format('woff2');
  unicode-range: U+0100-024F, U+0259, U+1E00-1EFF;
}

Glyphhanger这样的工具可以分析你的内容并生成最优子集。对于Google Fonts,google-webfonts-helper提供预子集化的文件。

设计健壮的系统字体栈

系统字体加载即时且提供优秀的回退。精心设计的字体栈确保即使自定义字体失败也能显示可读文本:

body {
  font-family: Inter, -apple-system, BlinkMacSystemFont, 
               'Segoe UI', Roboto, sans-serif;
}

使用CSS描述符匹配自定义字体和回退字体的度量:

@font-face {
  font-family: 'Inter';
  src: url('/fonts/inter.woff2') format('woff2');
  font-display: swap;
  size-adjust: 100.5%; /* 匹配回退字体度量 */
  ascent-override: 95%;
  descent-override: 25%;
}

使用Font Style Matcher计算这些值并最小化布局偏移。

预加载关键字体

预加载告诉浏览器在CSS中发现字体之前立即获取它们:

<link rel="preload" href="/fonts/inter-latin.woff2" 
      as="font" type="font/woff2" crossorigin>

关键考虑事项:

  • 仅预加载首屏字体
  • 包含crossorigin属性(字体必需)
  • 匹配@font-face声明中的确切URL
  • 避免预加载每个子集——这会违背子集化的目的

与内联关键CSS结合使用以获得最大效果:

<style>
  @font-face {
    font-family: 'Inter';
    src: url('/fonts/inter-latin.woff2') format('woff2');
    font-display: swap;
  }
</style>

策略性考虑可变字体

可变字体可以用一个文件替换多个文件,但它们并不总是更小。支持字重100-900的可变字体可能是50KB,而两个静态字重总计30KB。

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

/* 使用范围内的任何字重 */
h1 { font-weight: 750; }
p  { font-weight: 425; }

可变字体在以下情况下表现出色:

  • 需要多种字重或宽度
  • 字重之间的平滑动画
  • 随视口缩放的响应式排版

对于使用2-3种字重的简单网站,静态字体通常性能更好。

对Core Web Vitals的影响

正确的字体加载直接改善关键指标:

最大内容绘制(LCP):预加载和font-display: swap确保文本快速渲染,防止LCP测量延迟。

累积布局偏移(CLS):匹配字体度量和使用size-adjust消除字体加载时令人不适的文本重排。

首次输入延迟(FID):减少字体载荷并避免阻塞渲染的行为保持主线程响应。

结论

现代字体加载不是选择一个完美策略——而是结合适合你特定需求的技术。从WOFF2和font-display: swap开始。为关键字体添加子集化和预加载。使用系统字体栈作为可靠回退。仅在可变字体提供明显好处时才考虑使用。

最重要的是,测量影响。使用WebPageTest或Lighthouse验证你的字体策略确实改善了实际性能指标。美观的排版不应该以牺牲用户体验为代价。

常见问题

是的,系统字体栈提供出色的性能和原生渲染。现代系统字体如San Francisco和Segoe UI看起来很专业。但是,自定义字体有助于建立品牌标识和跨平台一致性。

使用unicode-range分割来仅加载所需的字符集。实施语言检测以预加载适当的子集。考虑使用Google Fonts API,它会根据文本内容自动提供优化的子集。

自托管让你完全控制缓存并消除第三方请求。CDN提供自动优化和跨站点共享缓存。为了隐私和性能可预测性选择自托管,为了便利和自动更新使用CDN。

每种字重通常增加15-30KB。加载四种字重意味着总计60-120KB。如果你需要超过三种字重,使用可变字体,或者为了最佳性能将自己限制在两种字重,如常规和粗体。

Understand every bug

Uncover frustrations, understand bugs and fix slowdowns like never before with OpenReplay — the open-source session replay tool for developers. Self-host it in minutes, and have complete control over your customer data. Check our GitHub repo and join the thousands of developers in our community.

OpenReplay