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 负责「换字体别跳」——这四件事都覆盖到,字体这一块基本不会翻车。
转载请注明出处,版权归原作者所有。
正式会员
认证极客






