KAIROS.WORKSPACE
技术宇宙 / ONLINE
返回文章列表
前端工程intermediate6 分钟

字体加载的取舍:CLS、FOUT 与"其实你不需要自定义字体"

Web 字体如何造成布局偏移与文本闪烁,font-display/size-adjust/预加载的组合拳,以及系统字体栈在内容站上的合理性论证。

#性能#字体#排版#CLS
排版
字号
行宽
目录 · 5 节

字体是品牌感的重要来源,也是首屏性能与布局稳定性的经典重灾区。本文把这条链路的取舍讲透,最后给出一个可能反直觉的结论。

问题怎么发生

HTML 到达 → CSS 解析 → 发现 @font-face → 发起字体请求 → 下载中… → 就绪 → 替换

下载期浏览器按 font-display 描述符的行为分三种:

  • block:最多约 3s 不可见文本(FOIT),页面有字但看不见——体验最差;
  • swap:先用回退字体立刻渲染(FOUT),字体到达后替换——可见闪烁;
  • optional:若网络快于约 100ms 用之,否则本次放弃——最克制。

真正的性能债在 swap 替换的那一刻:自定义字体与回退字体的字形宽度/高度不同,替换导致所有文本重排。发生在 LCP 元素上,直接吃 CLS。

组合拳:把 swap 的伤害降到接近无

1. 度量对齐(size-adjust)——让回退字体假装自己是自定义字体的宽高:

@font-face {
  font-family: 'Headings Fallback';
  src: local('Arial');
  size-adjust: 104.5%; /* 匹配目标字体的平均字宽 */
  ascent-override: 90%; /* 匹配行高度量 */
  descent-override: 20%;
  line-gap-override: 0%;
}

回退与真实字体度量一致时,swap 前后布局不再偏移——现代在线工具(或 fontaine 库)可自动算出这些值。

2. 提前发现——<link rel="preload"> 只用于首屏关键的单个字重;给五个字重全 preload 等于用带宽换焦虑。

3. 子集化——展示型标题字体只用到几十个字符时,unicode-range 子集能把 300KB 砍到 20KB。

4. 缓存——字体文件名带哈希、Cache-Control: immutable,回访用户零开销。

回到那个反直觉问题:你真的需要它吗

对个人内容站的冷静成本收益:

  • 正文:系统字体栈(-apple-system, Segoe UI, Roboto, system-ui + 中文回退 PingFang SC, Microsoft YaHei)就是用户设备上已下载、已针对屏幕调校过的最优解。零请求、零 CLS、跨平台只是略有差异——而正文跨平台一致性对阅读体验并不关键。
  • 代码字体ui-monospace, SFMono-Regular, Menlo, Consolas, monospace 覆盖现代系统,无需下载。
  • 标题/品牌字:这是唯一值得权衡的位置——一个子集化的展示字体,若品牌收益真实,配好 size-adjust 后其代价约等于一次 20KB 请求。

一个常见决定(也是许多内容站最终收敛的方案):正文与代码全系统栈,标题可选一款子集化展示字体——首屏只剩 HTML/CSS 本身,字体链路从关键路径上整个消失。这不是没有审美,是把审美预算花在刀刃上。

决策清单

  • 这个字体是“品牌必需”还是“审美惯性”?
  • 正文用系统栈是否完全可接受?(通常是)
  • 必下的字体有没有做子集化 + size-adjust 回退配对?
  • font-display 是否明确设过,而不是跟着默认 auto 赌运气?
  • CLS 报告里有没有字体替换贡献的偏移?(DevTools → Performance → Layout Shift 记录可归因)

小结

字体优化的公式:能不下就不下(系统栈);必须下就小(子集化)+ 快(预加载单个关键款)+ 稳(size-adjust 对齐度量 + swap)。内容站的性能洁癖最终大多收敛到同一个选择:把字体请求从“性能问题”降级成“品牌问题”——只有后者才值得付那几 KB。

Conversation

评论与互动

正在加载评论…

提交后需审核,不会立即公开。

Keep exploring

继续探索