CSS字体加载与回退机制。

陈先生
陈先生 正式会员正式会员认证极客认证极客
发布于 2026-07-21 11:12 ·64 浏览 ·0 回复

网页字体的回退行为由三件事共同决定: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 负责「换字体别跳」——这四件事都覆盖到,字体这一块基本不会翻车。

本文转载自 Clara轻量论坛系统,原文地址:https://www.leleweb.cn/thread-412.html
转载请注明出处,版权归原作者所有。

全部回复 0

还没有回复,来抢沙发~