font-display: swap 是防止 foit 的最有效低成本方案,它强制浏览器用系统字体立即渲染,自定义字体加载完再替换;未显式声明时浏览器按 auto(等同 block)处理,导致文本先隐藏约 100ms、再最多等待 3 秒,引发首屏白屏。

直接写 font-display: swap 是防止 FOIT(文字隐形)最有效、成本最低的手段——它不改 HTML,不加 JS,就能让文本立刻用系统字体显示,等自定义字体加载完再替换。
为什么没写 font-display 就等于白屏 3 秒?
浏览器对 @font-face 的默认行为是 font-display: auto,实际等同于 block:先隐藏文本约 100ms,再最多等待 3 秒加载字体,失败才回退到备用字体。你看到的“空白段落”,不是网络慢,是浏览器在严格执行规范。
- 本地开发时可能感觉正常,是因为缓存快;上线后用户常卡住几秒
- Network 面板里字体请求已完成,但文字仍不出现——说明是渲染被阻塞,不是加载失败
- Lighthouse 报 “Ensure text remains visible during webfont load”,就是这个原因
swap 必须正确写在 @font-face 里
它不能写在 CSS 类中,也不能靠继承生效,必须每条 @font-face 规则都显式声明。
- 错误写法:
@import url("fonts.css");—— 解析延迟,font-display生效滞后 - 漏掉
format("woff2"):旧版 Safari/Edge 直接忽略整条规则,退回到默认阻塞行为 - 路径写相对地址如
url(./fonts/xxx.woff2):在 file:// 协议或某些 WebView 下 404,规则失效
光写 swap 不够,还得配 preload 和 fallback
swap 解决“怎么显示”,但不解决“什么时候开始下载”。没预加载,弱网下替换窗口可能拖到 1–2 秒。
-
<link rel="preload" href="/fonts/Inter.woff2" as="font" type="font/woff2" crossorigin>——crossorigin和as="font"缺一不可 - fallback 字体链要度量接近:比如中文用
"PingFang SC", "Hiragino Sans GB", sans-serif,避免替换时跳动 - 中文字体务必子集化:全量字体常超 2MB,首屏只提取 200KB 内常用字,减少加载压力
移动端要更谨慎:optional 可能比 swap 更稳
iOS Safari 15.4 之前不支持 font-display,会直接回退到阻塞行为;中文字体体积大、替换易跳动,optional 主动放弃加载慢的字体,反而更可靠。
-
font-display: optional表示:只在 100ms 内就绪才用,否则全程用系统字体 - 需配合显式设置容器尺寸(
font-size、line-height、min-height),防布局抖动 - 用
document.fonts.load()主动监听加载结果,失败时可降级提示或补救











