font-display swap是什么?避免字体引起的布局偏移

font-display swap对比block、fallback和optional,讲解字体预加载、Google Fonts与自托管,以及如何防偏移。

目录

font-display swap(即font-display: swap)会告诉浏览器先用回退字体立即显示文字,等网页字体到达后再换上去。它去掉了不可见文字的等待,但不能消除布局偏移:当网页字体比回退字体更宽或更高时,文字会在替换时重新排版。要同时获得快速显示的文字和稳定的布局,请设置swap,尽早加载字体,并用size-adjust让回退字体的尺寸与网页字体一致。下面的章节会比较font-display的五个值,并给出每一步的代码。

font-display swap的作用

swap会给字体面设定“极短的阻塞期和无限的替换期”(MDN,最后修改于2026年9月10日)。简单说,文字不会被长时间隐藏,只要网页字体加载完成,无论多晚都会被使用。

MDN对这两个时段的定义如下。在阻塞期内,使用尚未加载字体的元素必须以不可见的回退字体渲染。在替换期内,它会以可见的回退字体渲染,而及时加载完成的字体会正常使用。常用的说法是FOIT(不可见文字闪烁,即阻塞期)和FOUT(无样式文字闪烁,即替换)。

如果不设置font-display,就由浏览器决定。web.dev的字体指南(2022年10月)称,基于Chromium的浏览器和Firefox在字体未加载时最多会阻塞文字渲染3秒,而Safari会无限期阻塞(web.dev)。请把这视为这些版本的行为,并检查你所支持的浏览器。

font-display自2020年1月起已是新近可用的Baseline功能,自2022年7月起广泛可用;web-features列出的支持版本为Chrome 60、Firefox 58、Safari 11.1和Safari iOS 11.3(web-features 3.44.0,2026年10月22日)。

@font-face {
  font-family: "Brand";
  src: url("/fonts/brand.woff2") format("woff2");
  font-weight: 400;
  font-style: normal;
  font-display: swap;
}

font-display各值的比较

五个值的区别只在于文字保持不可见的时间,以及晚到的字体仍可替换进来的时间。以下是web.dev表格中的各时段:

值 阻塞期 替换期 适用场景
auto 由浏览器决定 由浏览器决定 没有明确选择(默认)
block 2到3秒 无限 很少使用:字体加载期间文字保持隐藏
swap 0 ms 无限 必须使用网页字体的标题和品牌文字
fallback 100 ms 3秒 折中方案:阻塞期很短,替换期也较短
optional 100 ms 无 正文,此时稳定的布局比精确的字体更重要
font-display 各取值的阻塞期与替换期:auto 由浏览器决定,block 隐藏文字 2 到 3 秒,swap 没有阻塞期,fallback 阻塞 100 毫秒、可替换 3 秒,optional 阻塞 100 毫秒且不再替换

web.dev建议,当文字必须快速显示并使用网页字体时,使用swap,前提是字体能够足够早地送达,不会引起布局偏移。性能最好的选择是optional,代价是如果字体到达得太晚,就不会使用网页字体。web.dev建议品牌和有特色的元素使用swap,正文使用optional(web.dev)。这种划分是一种建议,并非规范的要求。

Lighthouse也会检查这一点。其“Font display”洞察(2025年10月)要求你将font-display设为swap或optional,以保持文字可见;对于swap,它会建议使用字体度量覆盖来减少布局偏移(Chrome for Developers)。

预加载真正重要的字体

预加载会更早开始下载字体,从而缩短停留在回退字体上的时间。自托管字体的预加载需要as="font"、MIME类型和crossorigin:

<link rel="preload" href="/fonts/brand.woff2" as="font" type="font/woff2" crossorigin>

MDN指出,字体预加载必须设置crossorigin,而且它必须与请求的CORS模式一致,“即使该请求并非跨域”,因为字体是以匿名CORS模式获取的(MDN,2026年5月)。

预加载只用于首屏所需的字体。web.dev警告,预加载会忽略unicode-range声明,因此如果要使用,应谨慎地只加载一种字体格式。对于不需要立即使用的字体,可以不预加载。预加载自2021年1月起已是新近可用的Baseline功能,自2023年7月起广泛可用(web-features 3.44.0)。

Google Fonts还是自托管

使用Google Fonts时,请在样式表URL中加入display参数,并尽早连接两个字体源:

<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Crimson+Pro&display=swap" rel="stylesheet">

display参数接受同样的五个值。Google的文档称默认值为auto,而且通常更适合使用其他值(Google Fonts,2024年7月更新)。字体文件的preconnect需要crossorigin,因为“与样式表不同,字体文件必须通过CORS连接发送”(web.dev)。

Google Fonts和自托管哪个更快?web.dev认为差别并不像看上去那样“泾渭分明”:在它引用的Web Almanac数据中,第三方字体的渲染速度比第一方字体更快,而自托管在使用CDN和HTTP/2时更有可能胜出。如果选择自托管,优化就由你负责:提供WOFF2(web.dev称它比WOFF压缩效率高30%),用unicode-range做子集化,并先检查字体的许可证。

用回退字体防止布局偏移

替换时的布局偏移,是因为回退字体与网页字体的度量不同,解决办法是调整回退字体使其匹配。web.dev的CLS指南把“渲染得比初始回退字体更大或更小的字体”列为布局偏移的原因之一,而良好的CLS值是0.1或以下(web.dev,2023年4月)。关于所有原因的更多内容,见累积布局偏移指南。

标题在较窄的备用字体下占两行;换成更宽的网页字体后变成三行,下方按钮被向下推,这就是布局偏移

工具是第二个@font-face,它指向本地字体并对其进行缩放。size-adjust按百分比缩放字形轮廓和度量,而ascent-override、descent-override和line-gap-override用于设置行框:

@font-face {
  font-family: "Poppins fallback";
  src: local("Arial");
  size-adjust: 60.85%;
}

body {
  font-family: "Poppins", "Poppins fallback", sans-serif;
}

60.85%只适用于Poppins搭配Arial这一组字体,因此你需要自己计算。Chrome的文章称,size-adjust是网页字体的平均字符宽度除以回退字体的平均字符宽度(在此例中为538.01 / 884.14 = 60.85%),并且在大多数情况下只能近似计算;三个覆盖描述符则由网页字体的ascent、descent和line gap除以每em单位数以及size-adjust的值计算得出。可以用该文章(2023年2月更新)列出的工具之一为你的字体计算,包括Fontaine、Capsize、next/font(自Next 13起)或@nuxtjs/fontaine(Chrome for Developers)。在依赖计算结果之前,请先在浏览器中检查效果。

按描述符划分的支持情况,根据MDN browser-compat-data 8.1.7(2026年10月):

描述符 Chrome Firefox Safari
size-adjust 92 92 17
ascent-override、descent-override、line-gap-override 87 89 不支持

web-features没有将三个覆盖描述符列为Baseline,因此iPhone上的Safari会忽略它们,仍可能出现轻微偏移。回退字体也会因设备而异:Chrome的文章指出,Android上的Roboto回退字体需要与Arial不同的值。

对手机上LCP文字的影响

当最大的元素是文字时,网页字体可能会延迟最大内容绘制。web.dev的LCP指南称,只有在阻塞期结束后,文字才算已渲染;因此在使用网页字体的文字出现之前,较小的元素可能被报告为LCP。它把<p>和<h1>等块级文字元素视为LCP候选,良好的LCP为2.5秒或以下(web.dev,2025年9月)。

如果页面的LCP是网页字体的标题,那么在较慢的手机网络上,字体下载会与其他所有资源争抢带宽,这使得swap或预加载值得花这份功夫。如何阅读LCP元素,见Lighthouse移动端审核指南;LCP变慢的其他原因,见移动页面速度测试指南。

在手机尺寸的页面上检查结果

修改之后,在冷加载状态下查看页面。在手机宽度下禁用缓存并重新加载,观察字体到达时标题是否会跳动。Mobile View Extension会以手机的用户代理、屏幕尺寸和像素比重新加载标签页,因此你可以看到移动端布局,以及显示的是哪种回退字体。但它不测量加载时间、布局偏移或任何速度指标,而且使用的是你桌面端的字体和网络,因此手机上的回退字体看起来可能不同。请用Lighthouse和现场数据进行测量。

常见问题

font-display swap做什么?

它会先用回退字体立即显示文字,等字体加载完成后再替换为网页字体,而替换可以发生在任何时候,没有时间限制。阻塞期极短,替换期无限(MDN)。

font-display选swap还是optional更好?

没有哪一个在所有情况下都更好:swap总会以你的网页字体结束,但可能引起布局偏移;optional可以避免偏移,但在加载较慢时可能跳过网页字体。web.dev建议品牌使用swap,正文使用optional。

font-display swap会导致布局偏移吗?

可能会。当回退字体与网页字体的度量不同时,替换会改变文字的大小和行高,从而移动内容;web.dev把这列为布局偏移的原因之一。用size-adjust让回退字体匹配,可以减少这种影响。

网页字体需要预加载吗?

预加载首屏所需的一到两个字体,并设置as="font"和crossorigin。web.dev建议谨慎使用,因为预加载会忽略unicode-range。

用手机视图查看你的下一个网站。

添加扩展,在你正在使用的标签页中直接打开设备预览。