font-display: optional 才是移动端更稳的选择,它避免FOIT白屏和FOUT抖动,仅在100ms内就绪时使用字体,否则全程用后备字体,并需配合preload首屏子集、显式设置文本容器尺寸及Font Loading API监控失败。

font-display: swap 在移动端并不能解决白屏,反而大概率加剧 FOIT(Flash of Invisible Text)或引发 FOUT(Flash of Unstyled Text)抖动——尤其对中文字体。它不是“优化”,而是把加载阻塞转嫁成视觉混乱。
font-display: swap 在移动端的实际表现
swap 告诉浏览器:「先用后备字体渲染,等自定义字体加载完立刻替换」。听起来合理,但移动端有三个硬伤:
- iOS Safari 15.4 之前版本不支持
font-display,直接回退到默认阻塞行为(FOIT 白屏) - 中文字体文件普遍 ≥2MB,3G 网络下加载常超 3 秒;swap 期间用户看到的是系统字体,替换瞬间触发重排重绘,文字跳动明显
- 若字体加载失败(如 CDN 404、HTTPS 混合内容被拦截),浏览器不会降级提示,而是持续用后备字体——设计师指定的标题粗体永远出不来
为什么 font-display: optional 才是移动端更稳的选择
font-display: optional 的逻辑是:「只在字体能在 100ms 内就绪时才用;否则直接放弃,全程用后备字体」。这对移动端更务实:
- 彻底规避 FOIT:不等、不闪、不跳,首屏文本秒出
- 降低资源竞争:字体加载请求仍发出,但不参与渲染关键路径,CSS/JS/图片能优先抢带宽
- 兼容性好:Chrome 87+、Safari 15.4+、Firefox 94+ 均支持,旧版自动忽略该声明,回退到原生行为(需配合其他策略兜底)
必须搭配的三项实操动作
单写 font-display: optional 不够,移动端要组合出效果:
立即学习“前端免费学习笔记(深入)”;
- 只
preload首屏必需的 WOFF2 字体,例如:<link rel="preload" href="/fonts/title-bold.woff2" as="font" type="font/woff2" crossorigin>;别 preload 正文、斜体、数字等非首屏字重 - 用子集工具(如
pyftsubset或在线服务)提取首屏汉字,把字体从 2.3MB 压到 ≤200KB;全量字体仍保留,但仅用于后续异步加载 - 所有文本容器必须显式声明
font-size、line-height、height(或min-height),否则后备字体与自定义字体度量差异会导致布局抖动
CSS Font Loading API 是唯一能抓到失败的手段
靠 font-display 无法知道字体到底加载成功没。真正要监控,得用原生 API:
document.fonts.load('1em "MyTitleFont"', '测').then(() => {
document.documentElement.classList.add('fonts-loaded');
}).catch(() => {
console.warn('标题字体加载失败,已降级');
});
这个 document.fonts.load() 调用会触发 loading、loadingdone、loadingerror 事件,只有这里才能拿到具体错误码(比如 NetworkError 或 ParseError)。没有它,字体崩了你根本收不到告警。
最易被忽略的一点:字体加载失败时,font-display: optional 不会触发任何回调,也不会抛错——它安静地、彻底地放弃。想定位问题,只能靠 document.fonts + 事件监听,别无他法。


















