页面文字先空白为FOIT,先系统字体后跳变为FOUT;本质是@font-face加载与渲染的竞态问题。Chrome Network→Fonts可查请求状态,Rendering→Paint flashing可观察重绘闪动。

字体加载时出现 FOIT 或 FOUT 怎么判断
页面刚打开时文字先空白(FOIT)或先用系统字体显示再跳变成目标字体(FOUT),本质是 @font-face 加载时机与文本渲染的竞态问题。Chrome DevTools 的 Network → Fonts 面板能确认字体是否被请求、是否阻塞渲染;Rendering → Paint flashing 可观察重绘区域——若文字区域反复闪动,大概率是字体回退触发了 layout 重排。
font-display 取值对闪烁行为的直接影响
font-display 是解决该问题最直接的 CSS 控制开关,它决定浏览器如何权衡「等待自定义字体」和「立即显示文本」:
-
font-display: block:强制等待字体加载完成(最长 3s),期间显示空白——易引发 FOIT,不推荐 -
font-display: swap:立即用系统字体渲染,字体加载完后无过渡替换——最常用,但可能造成视觉跳变(FOUT) -
font-display: fallback:短等待(~100ms)后降级,加载成功后仍可替换——平衡体验与性能,适合正文 -
font-display: optional:仅当字体已缓存才使用,否则全程用系统字体——适合非关键字体,如装饰性标题
建议正文用 fallback,Logo 或主标题可酌情用 swap,避免全站统一设为 swap 导致多处跳变。
预加载关键字体 + 避免跨域 CORS 干扰
即使设了 font-display: swap,若字体文件本身加载慢或被跨域策略阻塞,仍会延长 FOUT 周期。两个关键点:
立即学习“前端免费学习笔记(深入)”;
- 在
中用提前发起字体请求(注意必须加crossorigin,否则字体加载会被视为跨域失败而静默丢弃) - 确保字体服务响应头包含
Access-Control-Allow-Origin: *(或指定域名),否则 Chrome 会拒绝应用该字体,回退到系统字体且控制台报错Font from origin 'xxx' has been blocked from loading by Cross-Origin Resource Sharing policy - 优先使用
.woff2格式,体积比.woff小 30%+,加载更快;避免在 CSS 中同时声明多个格式(如woff2, woff, ttf),浏览器仍会按顺序尝试下载,浪费请求数
JavaScript 主动控制字体就绪状态(进阶)
当需要更精细地控制字体切换时机(比如动画入场、避免段落高度突变),可用 document.fonts.load() + fontfaceobserver 库或原生 API:
document.fonts.load('16px "YourFont"', 'a').then(() => {
document.documentElement.classList.add('fonts-loaded');
});
配合 CSS:
.fonts-loaded h1 { font-family: "YourFont", sans-serif; }
h1 { font-family: system-ui, sans-serif; transition: font-family 0.2s; }
注意:document.fonts.load() 不会触发重排,但需确保调用时机在 DOM ready 后;若字体未声明 unicode-range,浏览器可能因字符未命中而不触发加载完成回调——调试时可在 DevTools Console 手动执行 document.fonts.check('16px "YourFont"', '测试') 验证匹配性。
字体闪烁不是单点问题,而是加载策略、格式选择、CORS 配置、CSS 声明和 JS 协同的结果。最容易被忽略的是 crossorigin 属性缺失和 unicode-range 过窄导致的“看似加载成功实则未应用”。











