preload字体仍发两次请求是因为浏览器未复用,关键在于href与@font-face中src的url字符串必须完全一致,且需同时满足as="font"、crossorigin属性(无值写法)和type匹配。

preload字体时为什么还会发两次请求
不是预加载写错了,而是浏览器根本没复用——它把preload和@font-face当成两个独立资源处理了。常见现象是Network里看到同一个woff2文件出现两次,Status一次是200(preload),一次是from memory cache或from disk cache(CSS触发),但如果你看到第二次是200或304,说明复用失败,等于白加。
-
href路径和@font-face中src: url(...)的字符串不完全一致:大小写、斜杠结尾、查询参数(如?v=1.2)差一个字符都不行 - 漏写
crossorigin属性:哪怕字体同源,也必须显式写crossorigin(无等号无值),否则预加载结果被丢弃,@font-face只能重新发起带CORS头的请求 -
as="font"写成as="fetch"或干脆没写:浏览器直接忽略preload,后续仍走常规流程 -
type="font/woff2"和实际服务端返回的Content-Type不匹配:比如返回application/font-woff2,而你写了font/woff2,部分CDN会拒绝复用
如何让preload和@font-face真正绑定复用
浏览器只认“URL字符串完全相等 + as="font" + crossorigin + type匹配”这四件事,缺一不可。它不归一化路径,也不忽略查询参数。
- 打开开发者工具 → Elements → 找到
@font-face块,复制src: url(...)里的完整路径(包括https://、大小写、?t=abc等),粘贴到<link rel="preload">的href里,一字不改 -
<link>必须放在<meta charset>和<title></title>之后、所有CSS<link rel="stylesheet">之前——晚于CSS引用,浏览器解析到样式时已经错过预加载时机 - 确保
@font-face声明里src顺序和你预加载的格式一致:如果src里woff2排第一,就只预加载woff2;别预加载woff却指望它覆盖woff2 - 构建工具(如Vite/Webpack)注入哈希时,务必让
preload的href和CSS里url()同步更新,不能只改一处
验证是否真的复用成功
别只看Network里有没有请求,要看它是不是被复用。关键指标不是“有没有下载”,而是“第二次是不是缓存命中”。
- 刷新页面 → Network → 找字体文件 → 看
Initiator列:第一次应为Preload,第二次应为Stylesheet(说明CSS触发了复用) - 看
Size列:第二次必须显示from memory cache或from disk cache;若仍是数字(如24 KB)或0,说明没复用 - 在Console里运行
performance.getEntriesByType('resource').filter(r => r.name.includes('woff2')),对比fetchStart和requestStart时间差——如果差值小于50ms,基本没提前 - 禁用缓存再测一次:勾选DevTools → Network → Disable cache,此时若第二次仍是
200,说明复用链彻底断了
容易被忽略的兼容性细节
看似都对了,但Safari或旧版Chrome可能卡在某个隐性规则上。尤其当字体托管在CDN或自建服务时,header策略常成为最后一道墙。
-
crossorigin必须是无值写法:crossorigin✅,不是crossorigin=""⚠️,更不是crossorigin="anonymous"❌(后者在部分CDN上会因响应头校验失败而拒收) - 服务端必须返回
Access-Control-Allow-Origin: *或明确域名,且不能带Access-Control-Allow-Credentials: true(字体不允许凭据) - 某些CDN(如Cloudflare)默认不透传
Access-Control-Allow-Origin,需手动开启CORS规则或改用Page Rule放行字体路径 - Safari对
font-display: swap支持良好,但若没配这个,即使preload成功,也会因FOIT导致“空白闪”而非“字体闪”,问题性质不同但表象相似
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!











