CSS字体加载与回退机制。
网页字体的回退行为由三件事共同决定:`font-family` 的兜底链、`@font-face` 里的 `font-display`、以及 `unicode-range` 分片。想同时拿到快的首屏和不跳动的排版,最稳的组合是「`font-display: swap`(或 `optional`)+ 完整系统字体兜底栈 + `preload` 关键字体 + `size-adjust` 抹平度量差」。
font-family 是一条从左到右的匹配链
结论:浏览器按 `font-family` 声明顺序从左往右找,碰到第一个「存在且覆盖该字符」的字体就用,全都不命中才落到通用族关键字。
所以链尾必须写 `serif` / `sans-serif` / `monospace` / `system-ui` 这类通用族。中英混排时西文字体通常不含汉字,浏览器会自动往后继续找有汉字覆盖的字体,因此「西文在前、中文在后」是正确姿势:
body {
font-family: "Inter", "PingFang SC", "Microsoft YaHei",
system-ui, -apple-system, sans-serif;
}
两个易踩的坑:含空格的字体名必须加引号,否则整条声明可能被解析器丢弃;不要在 CSS 里用 `@import` 引字体,它会阻塞样式解析,直接写 `@font-face` 或 HTML 里的 `<link>` 更合适。
font-display 决定「等还是不等」
结论:`font-display` 是回退机制的核心开关,它用 block 期(字体没到先用不可见占位)和 swap 期(先用回退字体渲染、字体到了再换)两个时间段描述浏览器的取舍。
- `block`:约 3s 阻塞期,文字不可见(FOIT),随后无限等待交换;
- `swap`:0 阻塞期,立刻用回退字体显示,字体随时到达随时换(FOUT),最常用的折中;
- `fallback`:约 100ms 阻塞期 + 约 3s 交换期,超时就不再换;
- `optional`:约 100ms 阻塞期、无交换期,字体没赶上这一屏就直接用回退,但浏览器会缓存它供下次导航使用——首屏最快,代价是首次访问看不到自定义字体;
- `auto`:交给浏览器决定,通常接近 `block`。
正文文字建议 `swap`,装饰性标题字体建议 `optional`。不支持该属性的老浏览器会直接忽略,继续按默认行为走。
unicode-range:让中文只下载用到的那一片
结论:中文字体动辄几 MB,靠 `unicode-range` 拆成子集分片后,浏览器只会下载页面实际用到的那几片。
@font-face {
font-family: "Noto Sans SC";
src: local("PingFang SC"), url("noto-sc-subset.woff2") format("woff2");
unicode-range: U+4E00-9FFF;
font-display: swap;
}
`src` 里的 `local()` 优先复用用户本机字体,命中就一个请求都不发。自托管中文子集可用 `pyftsubset`(fonttools)按常用字表切分,商用字体注意授权是否允许切割和自托管。
preload + size-adjust:把「闪一下」压到最小
结论:字体文件不会被 HTML 解析器主动发现,必须显式 preload;而回退字体与自定义字体的字面高度、行高不同会造成 CLS,用度量覆盖可以对齐。
<link rel="preload" as="font" type="font/woff2"
href="/fonts/inter.woff2" crossorigin>
`crossorigin` 不能省——字体请求始终是 CORS 请求,缺了它浏览器会下载两次。度量对齐则是给回退字体单独定义一个 `@font-face`:
@font-face {
font-family: "Inter Fallback";
src: local("Arial");
size-adjust: 107%;
ascent-override: 90%;
descent-override: 22%;
}
然后把 `"Inter Fallback"` 插在自定义字体后面。像 Clara BBS 这类无编译缓存、保存即生效的轻量系统,改完字体栈刷新页面就能看到效果,不需要清缓存或重新构建,调试起来反而更直接。
怎么确认自己写对了
结论:用 DevTools 和 CSS Font Loading API 两条路验证,比肉眼盯着看靠谱。
DevTools 的 Network 面板按 `Font` 过滤,重点看两件事:同一个字体是不是被下了两次(多半缺 `crossorigin`)、被 preload 的字体有没有真的被用上(没用上就是白占带宽)。控制台里 `document.fonts.check('16px "Inter"')` 返回布尔值判断字体是否可用,`document.fonts.ready.then(...)` 可在字体全部就绪后做二次布局微调。Performance 面板里看 CLS 归属元素,字体交换造成的位移基本都能定位到具体节点。
收个尾:回退链负责「找不到怎么办」,`font-display` 负责「等不等」,`unicode-range` 和 `preload` 负责「少下点、早点下」,`size-adjust` 负责「换字体别跳」——这四件事都覆盖到,字体这一块基本不会翻车。
转载请注明出处,版权归原作者所有。
星耀SVIP
管理员
黑卡会员





