必须显式设置 font-display,否则默认 block 导致首屏文字白屏约3秒;推荐用 swap/fallback/optional,禁用 block;preload 字体需加 crossorigin;只用 WOFF2 格式并子集化;用 document.fonts.load() 检测加载失败。

不加 font-display 就等于默认启用 block,首屏文字大概率白屏 3 秒——这不是 bug,是浏览器的规范行为。
为什么必须显式设置 font-display
浏览器对 @font-face 的默认行为是 font-display: auto,实际等同于 block:先隐藏文本约 100ms,再等待字体加载最多 3 秒,失败才 fallback。这对内容型页面就是 FOIT(文字消失)。你看到的“空白几秒才出字”,不是网络慢,是浏览器在守规矩。
常见错误现象:
- 本地开发时一切正常(缓存命中快),上线后用户反馈“页面卡住”
- Network 面板里字体请求已完成,但文字仍不显示
- 用 Lighthouse 测出“避免不可见文本闪现(FOIT)”告警
推荐策略:
立即学习“前端免费学习笔记(深入)”;
-
swap:最常用,立即显示备用字体,加载完立刻替换(注意可能触发重排) -
fallback:给自定义字体约 100ms 加载窗口,超时就永久用备用字体,适合正文类文本 -
optional:只在字体已缓存或网络极快时才用,适合装饰性标题字,避免无谓请求 - 绝对别用
block,也别省略font-display
preload 字体必须带 crossorigin 属性
<link rel="preload"> 是唯一能提前触发字体下载的 HTML 级手段,但漏掉 crossorigin,浏览器会直接忽略该请求——哪怕字体跟页面同源。
正确写法必须同时包含三项:
as="font"type="font/woff2"-
crossorigin(值为空即可,不用写crossorigin="anonymous")
示例:
<link rel="preload" href="/fonts/Inter.woff2" as="font" type="font/woff2" crossorigin>
注意:preload 不替代 @font-face,两者要共存:前者负责提前取,后者负责声明和应用;只对当前导航中「确定马上要用」的字体做 preload,比如首屏标题/正文所用字体。
WOFF2 是唯一应上线的格式
别再塞一堆 .ttf、.woff、.eot 回退了。截至 2026 年,所有主流浏览器(Chrome、Firefox、Safari、Edge)都原生支持 woff2,且压缩率比 woff 高 30% 以上。
错误做法:
- 在
src里堆砌多个格式,以为“多写几个更兼容” - 用未压缩的 TTF 直接上线,单文件动辄 2–4MB
- 没做子集化,字体文件含上万汉字但页面只用几百个
正确做法:
- 只保留
url("xxx.woff2") format("woff2")这一项 - 用
pyftsubset或在线工具生成汉字子集(如仅含 GB2312 常用字) - 确保服务器对
.woff2返回Content-Encoding: br或gzip
document.fonts.load() 是唯一可感知加载失败的方式
CSS 层面无法捕获字体加载失败,font-display 和 preload 都不提供回调。想做兜底提示、降级样式或埋点统计,必须用 JS 主动检测。
示例:
document.fonts.load('1em "Inter"', 'a').then(() => {
document.body.classList.add('fonts-loaded');
}).catch(() => {
console.warn('Inter font failed to load');
});
关键细节:
- 第二个参数是测试字符,不能为空字符串,建议用常见字母或汉字
- 不要在
DOMContentLoaded之前调用,否则document.fonts可能未就绪 - 它返回 Promise,但不阻塞渲染,适合配合 CSS 变量或 class 控制状态
最容易被忽略的一点:字体加载失败时,font-family 会静默回退到备用字体,页面看起来“没问题”,但品牌一致性已经丢失——而你完全不知道。



















