字体是品牌感的重要来源,也是首屏性能与布局稳定性的经典重灾区。本文把这条链路的取舍讲透,最后给出一个可能反直觉的结论。
问题怎么发生
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。