font-display: swap加了仍抖动,是因为fallback与web字体x-height、line-height、字宽等度量不一致导致cls;必须配合preload(带crossorigin/as="font")、format声明、size-adjust/ascent-override及同类型fallback字体链才能消除。

font-display: swap 为什么加了还是抖?
加了 font-display: swap 却仍有抖动,大概率不是策略错了,而是 fallback 字体和 Web 字体的度量不一致。浏览器虽然立即显示后备字体,但替换时若 x-height、line-height 或字宽差异超过 5%,就会触发 layout shift(CLS),用户感知为“文字跳一下”。这不是加载慢,是排版错位。
常见诱因包括:
- 后备字体链混用衬线与无衬线(如
"Georgia", "Inter") - 没声明
size-adjust或ascent-override等 OpenType 度量对齐属性 - 在外部 CSS 或内联样式中用
font-size: 0.9em强制缩放,放大了度量偏差
preload 必须带 crossorigin 和 as="font"
font-display: swap 只管“怎么显示”,不管“什么时候开始下载”。没 preload,字体请求可能被排在图片、JS 后面,弱网下延迟超 1 秒,swap 的“立即显示”就变成“等 1 秒后才换”,用户仍看到长时间 fallback。
正确写法必须同时满足三项:
-
rel="preload"+as="font"(否则浏览器不提升优先级) -
crossorigin属性(CORS 要求,缺它等于没 preload) -
type="font/woff2"(帮助浏览器跳过 MIME 类型 sniffing)
错误示例:<link rel="preload" href="/fonts/Inter.woff2"> —— 缺三项中的任意一项,预加载都失效。
@font-face 中 format() 声明不能省
很多项目写了 font-display: swap 却没生效,根源在 @font-face 的 src 没写 format("woff2")。旧版 Safari、某些 WebView 会直接忽略整条规则,退回到默认 font-display: auto,导致 FOIT 回归。
务必检查:
- 路径是否绝对(相对路径在
file://或某些 iframe 下 404) -
src是否只用一个url(),而没兜底多个格式(如 woff2 + woff) - 是否用
@import引入字体 CSS —— 预加载和解析都会延迟,且无法控制资源优先级
正确写法:@font-face { font-family: "Inter"; src: url("/fonts/Inter.woff2") format("woff2"); font-display: swap; }
中文字体必须子集化 + 布局锁定
中文字体文件动辄 2–5MB,全量加载必然拖慢首屏。即使开了 swap,fallback 字体(如 system-ui)和中文字体的字宽差异极大,FOUT 几乎必然伴随严重跳动。
必须做两件事:
- 用工具(如
pyftsubset或在线服务)按实际页面用到的汉字生成子集,体积可压到 200KB 以内 - 配合
font-optical-sizing: auto和font-variation-settings(如支持可变字体)做布局锁定,避免缩放破坏行高一致性
别指望 font-display: optional 解决中文问题——缓存命中率低,多数用户根本看不到自定义字体。
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!











