图片缓存需分层控制:静态图用Cache-Control设长期缓存,动态图用no-cache或no-store;避免query参数失效,优先嵌入路径版本;前端配合onerror、HEAD校验与load监听;Service Worker须按MIME分流、设独立缓存空间并清理。

图片缓存不是“开或关”的二选一问题,而是要按资源类型、更新频率、加载路径做分层控制——直接全局禁用 Cache-Control 或硬加时间戳,反而会拖慢首屏、浪费带宽、触发重复请求。
怎么用 Cache-Control 精确区分静态图与动态图
浏览器只认响应头,不认文件后缀。同一张 logo.png,可以被服务器返回两种完全不同的缓存策略:
- 运营 Banner 图(每周换一次):返回
Cache-Control: public, max-age=604800(7天) - 用户头像(可能随时改):返回
Cache-Control: no-cache, must-revalidate,强制每次校验ETag或Last-Modified - 实时截图(如摄像头预览):返回
Cache-Control: no-store,彻底禁止缓存
关键点在于:Nginx 或后端路由需根据 URL 路径规则匹配,比如 /static/images/logo/ 走长期缓存,/api/avatar/ 走协商缓存,不能靠前端 JS 临时打补丁。
为什么 query 参数(如 ?t=123456)在 CDN 场景下容易失效
很多开发者习惯在 img.src 后拼时间戳,但这在 CDN 或代理层可能被主动 strip 掉——特别是当 CDN 配置了 ignore_query_string 或开启「忽略参数缓存」时,pic.jpg?t=1625097600000 和 pic.jpg?t=1625097600001 会被视为同一个 key,仍返回旧资源。
立即学习“前端免费学习笔记(深入)”;
- 更稳的做法是把版本信息嵌入路径:用
/v2.1/images/chart.png代替/images/chart.png?v=2.1 - 构建工具(如 Webpack/Vite)可自动重写引用,保证 HTML 中的路径含哈希或语义版本
- 若必须用 query,确保 CDN 控制台明确开启
query string caching并保留关键参数名(如只保留v或hash)
如何让 img 标签自己感知缓存状态并降级处理
纯靠 HTTP 头无法解决“图片已更新但用户没刷新页面”的场景。需要前端主动配合:
- 监听
img的onerror,失败后尝试加随机参数重载(仅限内部可信路径) - 对关键业务图(如订单凭证),用
fetch()先 HEAD 请求,比对ETag是否变化,再决定是否更新img.src - 避免在
src变更后立刻读取naturalWidth—— 此时可能还是旧图尺寸,应监听load事件后再操作
示例片段:
const img = document.getElementById('receipt');
img.addEventListener('load', () => {
console.log('新图已加载,naturalWidth:', img.naturalWidth);
});
img.src = '/receipts/12345.png?ts=' + Date.now(); // 仅作 fallback,非主策略
Service Worker 缓存图片时最容易忽略的三个细节
一旦引入 SW,它就接管了所有图片请求,但默认行为往往和你预期相反:
- 缓存策略写成
cacheFirst却没配cacheName版本前缀,导致旧缓存永远不淘汰 - 响应流未 clone 就多次使用(如同时供 cache.put 和页面渲染),引发
TypeError: Response body is already used - 忘记监听
fetch事件中对destination === 'image'的过滤,把字体、JSON 也塞进图片缓存池,撑爆CacheStorage限额
真正可控的图片缓存,得在 SW 里按 MIME 类型分流、设独立缓存空间、并配合后台同步清理逻辑——而不是把所有资源丢进一个 cache.addAll() 了事。



















