font-display: swap 是第三方字体按需加载的底线配置,它使系统字体立即渲染、字体文件后台静默加载并就绪后无感替换,避免 foit;需显式声明、禁用 auto、慎用 optional,并配合 preload 与子集化优化。

font-display: swap 是第三方字体按需加载的底线配置
不加 font-display,浏览器就默认用 block 行为:文字先消失 100ms,再等最多 3 秒加载字体,失败才 fallback——这不是“没加载好”,是浏览器主动制造 FOIT(Flash of Invisible Text)。swap 才是真实意义上的“按需”:系统字体立刻渲染,字体文件后台静默加载,就绪后无感替换。
常见错误:
• 只写 @font-face 却漏掉 font-display
• 误用 fallback(100ms 加载窗口太短,非关键文字也容易永久 fallback)
• 在 Safari 14.1 以下或旧 Android WebView 中未降级处理(可加 font-display: optional 回退,但慎用)
实操建议:
• 所有第三方字体声明必须显式写 font-display: swap
• 不要依赖 auto,它在 Chrome/Firefox 中实际等于 block
• 若字体仅用于装饰性标题,且允许缓存命中才显示,才考虑 optional
preload 关键字体时 crossorigin 属性不能省
<link rel="preload"> 是唯一能让浏览器在 HTML 解析阶段就发起字体请求的方式,比 CSS 里的 @font-face 提前 200–500ms。但漏掉 crossorigin,这个 preload 就完全失效——哪怕字体文件和页面同源。
必须同时满足三项:
• as="font"
• type="font/woff2"
• crossorigin(即使同源也要写)
示例:<link rel="preload" href="/fonts/inter-bold.woff2" as="font" type="font/woff2" crossorigin>
注意:
• 只预加载首屏确定会用到的字重(比如标题用 Bold,正文用 Regular,就分开 preload)
• 不能和 font-display: optional 共存,后者会让浏览器跳过加载,preload 白做
• preload 不替代 @font-face,两者必须共存
字体文件本身要按使用场景拆分与子集化
一个 Inter Variable 字体文件动辄 300KB,但你页面可能只用 Regular + Bold 两个字重,且只显示英文、数字和基础标点。这时候“按需”不是靠 JS 判断,而是构建时切分。
实操路径:
• 用 pyftsubset(fonttools)或 Transfonter 在线工具生成 ASCII 子集,体积常压到原大小 15–20%
• 把不同字重拆成独立文件:inter-regular.woff2、inter-bold.woff2,CSS 中按需引用
• 避免把整套 variable font 当作单个资源加载,否则所有文字都要等那个大文件
额外提醒:
• 中文项目慎用全量思源黑体,优先用 Noto Sans CJK 的子集或阿里普惠体等轻量方案
• 字体文件放在 CDN 上,确保开启 Brotli 压缩和 HTTP/2 多路复用
字体加载失败时需要主动检测,而不是靠肉眼判断
用户看到文字用了系统字体,不等于字体加载成功——可能是网络失败、CORS 拦截、路径 404,或字体格式不被支持。浏览器不会报错,也不会触发事件,除非你主动查。
可靠方式是用 document.fonts.load():document.fonts.load('1em "Inter"', 'a').then(() => console.log('Inter loaded')).catch(() => console.log('Inter failed'));
关键点:
• 第二个参数传一个典型字符(如 'a' 或 '测试'),避免空字符串导致检测无效
• 必须在 @font-face 声明之后调用,否则字体名未注册
• 检测失败后可动态降级 class、打点上报,或切换备用字体栈
容易被忽略的是:字体加载状态无法通过 window.onload 或 DOMContentLoaded 判断,它和资源加载是异步解耦的——这也是为什么很多“优化了字体”的页面,其实根本没生效。
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!











