字体preload缓存不命中主因是url、crossorigin、content-type、cache-control四者未严格一致:路径大小写、查询参数、cors头缺失或不匹配、mime类型错误、缺少immutable头均导致无法进入font缓存池。

为什么字体 preload 缓存经常不命中
字体资源写对了 rel="preload",Network 面板里也看到请求发出去、状态码 200,但下一页或刷新后仍 FOIT(Flash of Invisible Text),说明缓存根本没复用——核心问题不是“没加载”,而是“没进对的缓存池”。浏览器把 as="font" 加载的字体放进独立的 font 缓存区,这个区域只接受三者完全一致的后续请求:URL 字符串(含查询参数)、crossorigin 属性值、服务端返回的 Content-Type 和 Cache-Control 头。
-
href路径大小写不一致(/fonts/Inter.woff2vs/fonts/inter.woff2)→ 视为不同资源 - 漏写
crossorigin,或写了但服务端没返回匹配的 CORS 响应头 → 字体进不了 font 缓存,只进 fetch 缓存(后续@font-face不会读取) - CSS 中
@font-face的srcURL 和 preload 的href差一个?v=1.2→ 浏览器认为是新资源,拒绝复用 - 服务端返回
Cache-Control: no-cache或缺失immutable→ 即使路径全对,下次导航时仍可能重新拉取
如何让字体 preload 真正进缓存并复用
关键不是“加了 preload”,而是让整个链路的标识对齐。必须同时满足以下四点:
-
link标签必须带as="font"和crossorigin(即使同域也要加,否则 Chrome/Safari 直接丢弃已加载字形) -
type属性要和服务端实际返回的 MIME 类型严格一致,例如服务端返回font/woff2,就写type="font/woff2",不能写type="font/woff" - CSS 中
@font-face的src值必须和href完全相同(建议全部用根相对路径,如/fonts/inter-latin.woff2) - 服务端响应头至少包含:
Cache-Control: public, immutable, max-age=31536000;字体尤其推荐加immutable,告诉浏览器“此资源永不变更”
验证字体是否真被缓存复用的实操方法
别只看 Network 面板有没有 preload 请求,重点看三个地方:
- 在 Network 面板筛选
Initiator = preload,确认 Priority 是Highest(不是Low);若 Priority 是 Low,大概率as没写或写错 - 刷新页面后,找到对应字体请求,看 Status 列是否为
200 (from memory cache)或304;如果是200且没有(from memory cache),说明没命中 - 打开 Application → Cache Storage,搜索字体文件名,看是否出现在
Font Cache下(Chrome 115+ 支持查看该缓存区);若只在Memory Cache或HTTP Cache里,说明@font-face没触发 font 缓存读取
容易被忽略的跨环境一致性陷阱
本地开发时一切正常,上线后字体又闪,往往是因为构建或 CDN 层悄悄改了路径或头信息:
- Webpack/Vite 构建后自动给字体加 hash 后缀(如
inter.a1b2c3.woff2),但 CSS 中@font-face的src还是旧路径 → 必须确保构建工具同步更新两者 - CDN 开启了自动压缩或重写功能,把
.woff2响应头改成application/octet-stream→ 会破坏type匹配,导致缓存隔离 - 使用
fetchpriority="high"或loading="eager"替代 preload → 对字体完全无效,这些属性只影响图片和 iframe
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!











