Skip to content

如何优化字体加载

要点速览

  • 识别 FOUT/FOIT:用 font-display 控制阻塞期与交换期,优先内容可见(swap/fallback)。
  • 优先 WOFF2:同等字体质量下体积更小;为非关键字体考虑 optional 放弃加载。
  • 预连接与预加载:preconnect 字体域,关键字体用 preloadcrossorigin,提升首屏可见速度。
  • 后备字体与连贯性:配置系统字体栈,匹配字宽/字重,降低切换闪烁;必要时用 font-size-adjust
  • 子集与 unicode-range:拆分常用字符与扩展字符,减少首屏体积;重要页面只加载需要的字形。
  • 变量字体:一个文件涵盖多粗细/斜体,减少请求与总体积,统一渲染表现。

快速上手

最小改动让“可见速度、闪烁控制、缓存与兼容”一次到位:

html
<!-- 1) 预连接 Google 字体域(或你的字体 CDN 域) -->
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />

<!-- 2) 预加载关键字体(示例:WOFF2,跨域必须带 crossorigin) -->
<link
  rel="preload"
  as="font"
  type="font/woff2"
  crossorigin
  href="/fonts/Inter-regular.woff2"
/>

<!-- 3) 定义 @font-face:优先 WOFF2,控制 FOUT/FOIT -->
<style>
  @font-face {
    font-family: "Inter";
    src: url("/fonts/Inter-regular.woff2") format("woff2");
    font-weight: 400;
    font-style: normal;
    font-display: swap; /* 内容优先:几乎无阻塞,先用后备字体,加载完成后替换 */
  }

  /* 可选:使用 unicode-range 为首屏只加载必要字形(示例:拉丁基本集) */
  @font-face {
    font-family: "InterSubset";
    src: url("/fonts/Inter-subset-latin.woff2") format("woff2");
    font-weight: 400;
    font-style: normal;
    font-display: swap;
    unicode-range: U+000-5FF; /* 拉丁字母范围,仅示例 */
  }

  /* 4) 应用字体与后备字体栈(匹配度高,切换更平滑) */
  :root {
    --brand-font: "Inter", "InterSubset", system-ui, -apple-system, "Segoe UI", Roboto,
      Helvetica, Arial, sans-serif;
  }
  body {
    font-family: var(--brand-font);
  }

  /* 5) 细节优化:弱网兼容与视觉一致性 */
  /* 非关键区域可以选择 fallback/optional,减少 FOIT 与后续闪烁 */
  /* 补充字体宽高一致性:在少数场景用 font-size-adjust 调平可读性 */
</style>

正确心智模型

  • “最佳实践”是相对场景的:内容优先的站点倾向 swap/fallback;品牌视觉极重的区域可用 block(谨慎)。
  • 体验权衡三角:内容可见速度、视觉一致性、整体性能;答案要能讲清权衡逻辑与取舍标准。
  • 工程协同:CDN 与缓存命中率、预连接/预加载、文件指纹与版本策略,与 CSS 配置同样重要。

核心问题引入

优化 Web 字体加载体验时,仅回答“使用font-display属性”并不完整,面试官真正考察的是对该属性背后FOUTFOIT两种字体加载现象的理解。

核心概念:FOUT 与 FOIT

浏览器加载自定义字体时,存在两种核心策略,各有优缺点:

现象名称加载策略核心问题
FOUT(Flash of Unstyled Text)先显示系统默认字体,待自定义字体加载完成后替换视觉不连贯(字体闪烁)
FOIT(Flash of Invisible Text)等待自定义字体加载完成后再显示文字,加载期间文字区域空白弱网环境下可能出现长时间白屏

注:过去不同浏览器对两种现象的默认处理不一致,导致用户体验不可控,font-display属性的出现正是为了将控制权交给开发者。

核心解决方案:font-display

font-display@font-face规则中的描述符,用于定义浏览器在“字体阻塞期”和“交换期”的行为,核心取值及适用场景如下:

swap:激进型(优先内容可见)

  • 行为:几乎无阻塞期,立即用后备字体显示文本,自定义字体加载完成后替换(主动选择 FOUT)。
  • 适用场景:新闻网站、博客等以“内容优先”的平台,需确保用户第一时间获取有效信息,短暂视觉闪烁可接受(目前最主流策略之一)。

block:保守型(优先品牌视觉)

  • 行为:给字体约 3 秒阻塞期,期间文本不可见(即 FOIT);3 秒内加载成功则显示自定义字体,失败则切换后备字体。
  • 适用场景:字体是品牌视觉核心的场景,如特殊字体设计的 LOGO、Slogan,短暂白屏可接受,避免品牌视觉失真。

fallback:折中型(平衡体验与性能)

  • 行为:极短阻塞期(约 100ms),100ms 内未加载完成则立即使用后备字体,且当前页面不再尝试替换自定义字体(避免后续闪烁)。
  • 适用场景:希望使用自定义字体,但不愿牺牲过多性能的场景——网速快时显示完美字体,网速慢时用后备字体,体验平滑无闪烁。

optional:性能优先型(非核心字体适用)

  • 行为:与fallback类似(短阻塞期),但赋予浏览器更大决定权:若判定网络连接不佳,直接放弃加载自定义字体。
  • 适用场景:“锦上添花”的字体(有无不影响核心功能和阅读),优先保证页面性能,如非关键区域的装饰性文字。

优化原则与延伸方向

核心原则

字体加载优化无“绝对最佳实践”,只有“基于场景的最优解”。回答面试官问题时,可按以下逻辑展开:

  1. 解释 FOUT 与 FOIT 的原理及优缺点;
  2. 结合font-display不同取值的行为;
  3. 阐述在“内容可见速度”“性能”“视觉一致性”三者间的权衡思路。

延伸优化方向

字体优化是系统工程,除font-display外,还需关注:

  • 字体格式选择(如优先使用体积更小的WOFF2格式);
  • 字体分包策略(拆分常用字符与特殊字符,减少加载体积);
  • 预加载(通过<link rel="preload">提前加载关键字体)。

常见误区与避坑

  • 只说 font-display:忽略 FOUT/FOIT 本质与网络条件;不能解释何时选 swap、fallback、optional。
  • 仅用 @import 引入远程字体:阻塞解析且不可控;应优先 <link> 并配合 preconnect/preload
  • 未配置后备字体栈:弱网/失败时出现 FOIT;后备字体与指标调平能显著改善体验。
  • 大而全的单一字体文件:未做子集拆分与 unicode-range,导致首屏体积过大。
  • 多个粗细/斜体分别请求:可用变量字体统一,减少请求与缓存碎片。

小结与后续

  • font-display 将控制权拉回:内容优先用 swap/fallback,非关键区域考虑 optional
  • 联动网络层优化:preconnectpreload 提升首屏可见速度;强缓存 + 指纹化控制更新。
  • 资源策略:WOFF2 优先、变量字体整合、子集与 unicode-range 仅加载必要字形。
  • 验证与监控:结合 Lighthouse/Performance 面板观察 FOUT/FOIT、首屏可见时间与总字节数,迭代调优。