在大多数网页字体优化场景中,font-display: fallback 通常是最稳妥的选择。它会在 500ms 内优先使用系统字体渲染页面内容,超时后直接放弃等待自定义字体,不阻塞页面布局,也不会强制替换字体。若想真正解决 CSS 自定义字体加载闪烁问题,还需要配合 preload 与规范的 @font-face 声明(包括 format("woff2")、不同字重分别声明等),这样基本可以覆盖 90% 的字体闪烁与文字抖动问题。

font-display: fallback 是多数场景的默认选择
更推荐的实践方式,通常就是直接使用 font-display: fallback,并结合 与标准写法的 @font-face 声明。这样一套配置下来,绝大多数网站遇到的字体加载闪烁、FOIT 或 FOUT 问题都能明显缓解。它未必是加载速度“最快”的方案,但通常是前端字体加载中“最稳定”的方案:浏览器会先在 500ms 内使用系统字体把内容显示出来,若超时则直接停止等待自定义字体,既不会拖慢首屏渲染,也不会执意执行字体替换。
中英文混排页面、首屏标题文案、输入框与表单文本都非常适合这种策略。不要以为只写一个 swap 就万事大吉——在弱网环境或 iOS Safari 中,swap 反而更容易让字体切换闪烁变得更明显。
fallback 的实际行为是:先显示后备字体,例如 sans-serif;如果自定义字体能在 500ms 内加载完成,就进行平滑替换;如果超时,则继续使用后备字体,不再继续等待。
相比 swap,它更克制:能避免用户长时间看到前后不一致的排版效果,也能规避 iOS Safari 对 swap 较激进的实现方式,例如延迟替换和渲染卡顿。相比 optional,它又更可靠:因为 optional 依赖浏览器预估字体是否能在 100ms 内就绪,而实际网络环境波动较大,这种预估很容易失准,尤其是在字体未命中的缓存场景下。
@font-face 里 font-display 怎么写才生效
font-display 必须写在每一个 @font-face 规则内部,而且只对当前这条字体声明生效。写在外层 CSS、 标签里,或者通过 JS 动态插入其他位置,都不会真正生效。
常见错误包括复制时漏掉引号、值拼写错误,例如写成 swop 或 swap:。一旦语法有误,整条规则都可能被浏览器直接忽略——结果不仅字体无法正常加载,连 fallback 策略都没有机会触发。
每个字重都需要单独声明对应的 @font-face,不能共用同一个 font-family 名称,却只给其中某一个字重配置 font-display。对于中文网页字体,Regular、Bold、Medium 等每一种字重都必须分别设置 font-display,否则部分字重仍然会走浏览器默认策略,导致字体加载表现不一致。
src 中必须包含 format("woff2")。如果漏写,Safari 和旧版 Edge 可能会直接忽略整条规则,回退到 auto 行为,从而引发 FOIT,也就是文字先不显示、等待字体下载完成的现象。
如果 WOFF2 字体文件路径涉及跨域加载,src 必须加 crossorigin,否则 iOS Safari 等浏览器可能会直接拒绝请求字体资源。
为什么写了 font-display 还在闪?检查 preload 是否配套
font-display 控制的是“字体如何显示”,而 preload 控制的是“字体什么时候开始请求”。如果只设置 fallback 却没有预加载,自定义字体资源仍可能被浏览器排到较低优先级,导致首屏文字已经渲染完成,字体请求才刚刚开始,这样依然会看到闪烁或替换延迟。
这段写法中的 crossorigin 不能省略。少了它,在跨域字体资源场景下,预加载通常无法生效,而且后续 @font-face 还可能直接触发 CORS 错误,导致字体加载失败。
预加载时只应针对首屏真实需要的字重,比如标题使用 Inter-Bold.woff2,正文使用 Inter-Regular.woff2。不要把所有字重、所有字体文件一次性都做 preload,否则反而会浪费带宽并影响关键资源调度。
如果站点已经使用了 HTTP/2 Server Push 或 HTTP/3,preload 的收益会进一步下降,甚至可能增加响应头复杂度。不过对于绝大多数普通网站来说,它依然是优化网页字体加载、减少字体闪烁的重要一环。
swap 不能单独解决文字抖动,必须配 size-adjust
font-display: swap 确实可以解决文字白屏的问题,但它无法单独解决字体切换造成的文字抖动。原因在于,swap 只是让系统字体先占位,等自定义字体加载完成后再替换;而系统字体与目标字体的 x-height、line-height、字宽等度量信息往往并不一致。浏览器会先按照回退字体分配空间,等真正替换时再重新排版,于是 CLS(累积布局偏移)就出现了。
常见表现包括:标题字体加载后整体下移几像素、导航区域图标与文字错位、按钮里的文本换行位置突然变化。本地环境看起来没问题,但线上页面出现抖动时,很大概率是因为线上用户使用了更高 DPR 屏幕,或者系统字体回退链与本地设备不同。
因此必须搭配合理的 font-family 回退链,例如:font-family: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;,这样可以避免某些 Android 设备回退到过粗的 Droid Sans,从而进一步放大布局偏移问题。
size-adjust 才是真正控制字体占位尺寸的关键参数,取值通常在 95%~105% 之间,需要根据实际页面效果进行测试。它目前仅在 Chromium 和 Safari 17.4+ 中生效,并且通常要和 font-display: swap 一起使用才更有意义。调试方式也很直接:在 Chrome DevTools 的 Elements 面板中右键目标文本 → Force font substitution → 选择系统默认字体,观察行高、基线和字宽的变化;然后打开 Rendering 面板 → 勾选 Layout Shift Regions,刷新页面,查看字体切换时是否出现红框闪烁;接着在 @font-face 中逐步尝试不同值:size-adjust: 100% → 102% → 103%,每次硬刷新验证布局偏移是否减弱。
size-adjust 只接受带百分号的数值,不能写成 1.03 或 103,否则浏览器不会按预期解析。
