配了八年字体,每次用户截图发群里说「页面是白的」才知道 FOIT 了——Chrome 111 把这件事用一行 CSS 彻底原生化了

配了八年字体,每次用户截图发群里说「页面是白的」才知道 FOIT 了——Chrome 111 把这件事用一行 CSS 彻底原生化了。做过网站的人都见过这个场景:用户进来说「你们页面是白的,内容加载不出来」,截图一看是 FOIT——Flash of Invisible Text,字体加载过程中文字被浏览器藏起来,最多藏 3 秒。工程师自己测的时候往往用的是公司光纤,根本触发不到这个场景,上线之后真实用户在 4G 或者信号差的地方才暴露出来。font-display 就是来解决这个问题的。这是 @font-face 的一个 descriptor,控制字体下载过程中文字怎么显示:css@font-face { font-family: MyFont; src: url(/fonts/myfont.woff2) format(woff2); font-display: swap;}一共有五种值:auto — 浏览器自己定,各家表现不一样,等于盲盒。block — 最多隐藏 3 秒,然后 swap。FOIT 风险最高,只适合 icon font 这种 fallback 根本没有意义的场景。swap(最常见)— 立即显示 fallback,等字体下载完再换过来。Google Fonts 默认就是这个策略。缺点是 fallback 和 web font 的 metrics 不同,swap 时 reflow 导致布局抖动,拖慢 CLS。fallback — 100ms 隐藏窗口,如果 3 秒内字体下载完成就 swap,否则继续用 fallback。block 和 swap 的折中。optional — 100ms 窗口,字体没下载完就放弃本次加载,fallback 用到底。字体后台静默缓存,下次访问几乎瞬间加载。零 CLS,是三个 Core Web Vitals 里最安全的选择。Google Fonts 默认给所有字体加 &display=swap。—实际数据:swap 最流行但 optional 才是最安全的2025 年 Web Almanac 数据:88% 的网站使用 web fonts,平均每个页面加载 4 个字体文件。但只有 12% 的网站 preload 字体,只有 0.5% 的网站用 optional。corewebvitals.io 统计了 189,915 个站点的 CrUX 数据:对 LCP 影响最大的是 block,用 block 的站点多的地方 LCP 通过率从 88% 跌到 82%。swap 占比最高(49.6%),LCP 通过率 88%→71%。optional 虽然只有 0.1% 的站点在用,但 LCP 通过率最高:86%→92%。—swap 的 CLS 问题可以修:fallback metrics matchingswap 最大的坑是 fallback 和 web font 的 metrics 不同,swap 时 reflow 拖慢 CLS。解法是用 CSS override descriptors 调整 fallback metrics:css@font-face { font-family: MyFont Fallback; src: local(Arial); size-adjust: 107%; ascent-override: 90%; descent-override: 22%; line-gap-override: 0%;}body { font-family: MyFont, MyFont Fallback, sans-serif;}Next.js 的 next/font 会自动生成这套 overrides。—三个下一步第一,查项目里有没有漏了 font-display@font-face,每个都要补上。优先级:正文字体 > 标题字体 > 装饰字体。第二,如果用了 swap,去 DevTools Network 面板看 swap 时有没有 reflow,有的话用 size-adjust + override descriptors 修 fallback。第三,装饰性字体、只在特定场景下才加载的字体,直接换成 optional。一句话:font-display 是前端最容易上手的性能优化之一,一个 descriptor 加进去,FOIT 直接消失,CLS 可以压到接近零。8 年了还在靠用户投诉才发现 FOIT,加这一行就够了。

评论区

0 条评论

登录后可评论。

阿柯·前端架构 11 阅读