webp/jpeg预加载未缓存命中,根本原因是preload的href与img实际请求url不一致,包括扩展名、查询参数、hash等任何差异都会导致缓存分离;服务端若未正确设置vary: accept头,也会破坏内容协商一致性。

为什么 WebP/JPEG 预加载后仍没缓存命中
常见现象是写了 <link rel="preload" as="image" href="/hero.webp">,但 Network 面板里图片请求仍显示 200(而非 200 (from memory cache) 或 304)。根本原因不是格式不支持,而是浏览器对不同 MIME 类型的缓存键处理严格——as="image" 仅保证按图片逻辑缓存,但不自动匹配 <img src> 的实际解析行为。
- WebP 和 JPEG 同路径但不同扩展名(如
/hero.webpvs/hero.jpg)会被视为完全不同的资源,即使服务端做了内容协商(Content Negotiation),浏览器也不会复用 -
<img src="/hero.jpg" srcset="/hero.webp 1x">中,浏览器选中 WebP 后实际发起的请求 URL 是/hero.webp,但如果你 preload 的是/hero.jpg,就无法命中 - CDN 或代理层若未正确设置
Vary: Accept响应头,会导致同一 URL 返回不同格式却共用缓存,反而破坏一致性
如何让 preload 真正匹配
的实际加载路径
关键在于:preload 的 href 必须和最终 <img> 发起的请求 URL 完全一致,包括协议、域名、路径、查询参数——任何差异都会导致缓存分离。
- 若用
srcset+sizes,预加载必须对应设备像素比和视口宽度下**实际被选中的那个 URL**,而不是“主图”;例如srcset="/hero-2x.webp 2x, /hero-1x.jpg 1x",在 Retina 屏上应 preload/hero-2x.webp - 避免在 preload 中使用带 hash 的路径(如
/hero.a1b2c3.webp),而<img>用的是无 hash 版本(/hero.webp)——二者不等价 - 服务端开启
Accept内容协商时,不要依赖客户端发送Accept: image/webp自动降级;应由前端明确决定加载哪个格式,并统一 preload 和src路径
Chrome DevTools 中验证缓存是否真正复用
别只看 preload 请求是否成功,要确认它是否被后续 <img> 实际复用。Network 面板筛选 Img 类型请求,检查以下三项:
-
Initiator列为img(不是parser或script),且Status显示200 (from memory cache)或304 - 对比 preload 请求与 img 请求的
Request URL和Response Headers中的ETag或Last-Modified是否一致 - 若看到两个独立的 200 请求(一个 initiator 是 preload,一个是 img),说明缓存未复用——大概率是路径或 Accept 头不一致
动态格式适配时的 preload 替代方案
当需要根据 supportsWebP 或 window.devicePixelRatio 动态决定加载哪种格式时,<link rel="preload"> 因其静态性无法胜任,必须改用 Image 对象。
- 先检测支持性:
const supportsWebP = document.createElement('canvas').toDataURL('image/webp').indexOf('data:image/webp') === 0 - 再构造 URL:
const url = supportsWebP ? '/hero.webp' : '/hero.jpg' - 然后 new Image() 预加载,注意顺序:
img.onload = () => {}必须在img.src = url之前设置,且要检查img.complete - 不要在页面初始化阶段一次性预加载所有可能格式,只预载当前设备确定会用的那个
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!











