<picture> 本身不参与缓存,真正决定图片缓存的是 Service Worker 的 fetch 拦截 + HTTP 缓存头 + URL 稳定性;需按 destination === 'image' 拦截并逐个缓存 srcset 中的动态 URL,且必须使用内容哈希或版本化路径确保缓存及时更新。

<picture> 标签本身不参与缓存决策,它只是个资源选择器;真正决定“哪张图被缓存、何时被加载、是否离线可用”的,是 Service Worker 的 fetch 事件拦截逻辑 + HTTP 缓存头 + 图片 URL 的稳定性。直接把 <picture> 和 PWA 缓存混为一谈,容易在部署后发现图片依然白屏或反复拉取大图。
Service Worker 必须按 destination === 'image' 拦截,不能只 cache.addAll()
很多人在 sw.js 的 install 阶段用 cache.addAll(['/img/logo.jpg']) 预缓存几张图,就以为所有 <picture> 里的图都能离线用了——错。浏览器对 <source> 中的 srcset 资源发起的请求,destination 仍是 'image',但 URL 是动态生成的(比如带 ?w=768 或 CDN 签名),根本不在预缓存列表里。
实操建议:
- 在
fetch事件中加判断:if (request.destination === 'image'),再走缓存匹配逻辑 - 缓存 key 用
new Request(request.url, { method: 'GET' }),避免因 header 差异导致 miss - 对
srcset中的每个候选 URL(如cat-320w.webp、cat-768w.jpg)都单独缓存,不要只缓存<img>的srcfallback - 缓存策略推荐:先 match cache,命中则返回;未命中则 fetch → put to cache → return,确保首次访问也能进缓存
渐进式 JPEG 与 WebP/AVIF 在 PWA 中的兼容性陷阱
服务端生成的渐进式 JPEG(progressive: true)能在弱网下逐层渲染,但它和 PWA 缓存是两件事:缓存的是整个文件字节流,浏览器解码时才触发渐进效果。而 WebP/AVIF 不支持传统渐进式,靠的是 <picture> 的 type 回退 + 更小体积来提速。
立即学习“前端免费学习笔记(深入)”;
常见错误现象:
- Chrome DevTools Network 面板看到
cat.jpg请求 size 显示 “from ServiceWorker”,但首帧仍黑屏 1.5 秒——说明缓存生效了,但图本身不是渐进式,浏览器得等整张解完才画 - 用
<source type="image/webp">时,Safari 15.4 以下直接跳过,降级到<img src="fallback.jpg">,但如果 fallback 不是渐进式,弱网下照样卡 - CDN 自动转 WebP 时,可能把原图的渐进标志丢掉,导致缓存里存的是 baseline JPG
实操建议:
- 服务端生成阶段必须显式开启渐进式:Node.js +
sharp用.jpeg({ progressive: true });PHP 用imageinterlace($img, 1) -
<picture>中的<source type="image/webp">后面,<img>的src必须指向一张服务端确认为渐进式的 JPEG,不能是自动转码结果 - 验证方式:用
file cat.jpg命令看输出是否含progressive JPEG字样,或用在线工具(如 jpeg.io)检查文件头
srcset + sizes 必须与 Service Worker 缓存粒度对齐
srcset 列表里的每个 URL 都是一个独立缓存项。如果 sizes="(min-width: 768px) 768px, 100vw",但你只缓存了 cat-1200w.jpg,而用户在 768px 宽视口下实际匹配到 cat-768w.jpg,这个请求就会穿透缓存直连网络——尤其在离线时直接失败。
实操建议:
- 用 Chrome DevTools 的 Device Toolbar 模拟不同设备宽度,打开 Network 面板刷新,记录真实发出的图片请求 URL,只缓存这些 URL
- 避免在
srcset里写无意义的宽度点(如320w, 375w, 414w...),每个多余项都意味着多一个要维护的缓存 key - 如果用 CDN 动态生成尺寸(如
https://cdn.com/cat.jpg?width=768),确保 Service Worker 的缓存 key 包含完整 query string,否则?width=768和?width=1200会被当成同一资源 - 对
type="image/avif"的 source,必须单独缓存其 URL,不能指望 fallback 的 JPEG 缓存覆盖它
最易被忽略的一点:缓存清理逻辑没跟上图片更新节奏。一旦某张图被覆盖(比如 CMS 后台替换了 banner.jpg),旧 URL 还在缓存里,用户看到的就是过期内容——这不是缓存没生效,而是缓存太生效了。必须配合内容哈希命名(banner.a1b2c3.jpg)或版本路径(/v2/banner.jpg),让 URL 变更成为缓存失效的自然信号。



















