Web 字体 font-display 与 CLS 优化
针对 loli.net 代理 gstatic 字体(推测为 Noto Sans SC / Roboto 等中文字体)出现的 140ms 文字不可见(FOIT) 问题,结合你提到的“进一步优化布局偏移(CLS)”,我提供一套从根源解决 + 终极 CLS 归零的实战方案。
第一步:动态调整 font-display 策略(选型)
不要盲目用 swap,根据业务场景选择:
- 博客/内容站:用
font-display: optional。浏览器会在 100ms 内决定是否等待字体。如果网络慢(140ms 属于较慢),直接使用后备字体,永不切换,完全消除 CLS 和闪烁,但首次访问可能看不到自定义字体。 - 品牌强依赖(Logo/标题):用
font-display: swap。文字立即显示,字体下载完成后切换。
鉴于你提及“进一步优化 swap”,说明你选择了 swap,下面给出终极防抖方案。
第二步:核心优化 —— 字体度量覆盖(Metric Overrides)
swap 导致 CLS 的根本原因是:Fallback 字体(如 sans-serif)的字符宽高与 Web 字体不一致。
通过 @font-face 中的 size-adjust、ascent-override、descent-override 强行拉伸后备字体,使其占位空间与目标字体完全一致。
具体操作(以 Noto Sans SC 为例,内联到你的 CSS 中):
/* 重新声明字体,覆盖原有 gstatic 的默认设置 */
@font-face {
font-family: 'MyOptimizedFont';
src: url('https://gstatic.loli.net/.../iJWZBXyIf....woff2') format('woff2');
font-display: swap;
/* ----- 关键:度量覆盖(数值需根据具体字体调整)----- */
/* 以下为 Noto Sans SC 相对于系统默认 sans-serif 的推荐矫正值 */
size-adjust: 98.5%; /* 宽度缩放,防止换行 */
ascent-override: 92%; /* 上升部高度矫正 */
descent-override: 28%; /* 下降部矫正 */
line-gap-override: 0%; /* 行间距归零,由 line-height 统一控制 */
}
/* 强制全局使用该字体栈,后备字体选系统最稳的 */
body {
font-family: 'MyOptimizedFont', -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, "PingFang SC", "Microsoft YaHei", sans-serif;
}
如何获取精确的覆盖数值?
使用 Google 官方工具fontaine(或在线计算器)。执行以下命令即可自动生成精确值:npx fontaine --src ./your-font.woff2 --fallback "Arial"
第三步:网络层提速(吃掉那 140ms)
既然 140ms 是“预估节省”,说明请求链路较长。在 HTML <head> 中加入:
<!-- 1. 提前连接,复用 TLS 握手 -->
<link rel="preconnect" href="https://gstatic.loli.net" crossorigin />
<!-- 2. 预加载关键字体(仅限首屏必要字体,不要滥用) -->
<link rel="preload" as="font" href="https://gstatic.loli.net/.../iJWZBXyIf....woff2" type="font/woff2" crossorigin />
配合 HTTP/2 Server Push(如果你控制 loli.net 代理端)或使用 Service Worker 缓存字体,二次访问直接 0ms 等待。
第四步:后备字体本地化降级(终极保底)
在 font-family 中插入系统字体别名,确保即使字体加载失败,渲染的也是高颜值系统字体:
/* 针对 Windows 和 macOS 优化显示 */
font-family: 'MyOptimizedFont', "PingFang SC", "Microsoft YaHei", system-ui, -apple-system, sans-serif;
第五步:验证与监控(修复后检查)
- Chrome DevTools -> Performance 重新录制,查看
Layout Shift是否归零。 - Lighthouse 重新跑分,确认 "Font display" 指标通过。
- 在 Network 面板设置 Throttling: Fast 3G,模拟弱网验证
swap切换是否丝滑。
针对 loli.net 的特殊建议(如果域名归属你控制)
如果你拥有 loli.net 服务器的控制权,最佳优化是直接删除 font-display 中的 block 期,在 nginx 响应头中强制添加:
add_header Link '<https://gstatic.loli.net/.../font.woff2>; rel=preload; as=font; crossorigin' always;
同时将 Cache-Control 设置为 1 年强缓存,让第二次访问完全无需请求。
执行完以上方案后,不仅 140ms 的 FCP 阻塞被消化,swap 带来的布局抖动也将彻底消失(CLS 增加 0.001 以内)。如果仍有特定字体的度量值需要精确计算,请提供具体的字体文件名(如 iJWZBXyIf 对应的完整名称),我可给出精确的 size-adjust 数字。