设置了Cache-Control后移动端图片还是重复请求是怎么回事?

萌新.柚溪 阅读 31

我在开发移动端混合应用时遇到个奇怪的问题。给图片资源设置了响应头Cache-Control: public, max-age=3600,但用Chrome开发者工具模拟移动端时,发现每次打开页面都会重新下载图片,控制台显示200 (from network)

我已经检查过服务端配置没问题,PC端桌面浏览器能正常命中缓存。尝试过清除浏览器缓存、修改max-age值甚至改用Expires头,移动端依然无效。这是不是移动端特有的缓存机制?还是我的代码哪里写错了?

请求代码如下:

const img = new Image();
img.src = '/assets/logo.png';
document.body.appendChild(img);

// 后续动态加载时
fetch('/assets/logo.png', {
  cache: 'force-cache' // 这个设置会不会有问题?
}).then(res => res.blob())
  .then(blob => URL.createObjectURL(blob));

移动端调试时发现第二次访问这个图片时,fetch请求依然走网络而不是缓存,难道是fetch和img标签的缓存策略不一致导致的?

我来解答 赞 8 收藏
二维码
手机扫码查看
2 条解答
Mr.世玉
Mr.世玉 Lv1
你这个问题其实挺常见的,我也踩过这个坑。核心问题出在 fetch 的 cache 选项和 HTTP 缓存机制之间的交互上。

首先确认一点:你在响应头设置 Cache-Control: public, max-age=3600 是正确的,PC 能缓存说明服务端没问题。

但移动端模拟器下看到 from network,不一定代表没走缓存。注意区分 Chrome DevTools 中的 "from network" 和 "from disk cache / memory cache"。有时候显示 from network 只是因为刷新触发了重新验证,实际可能是 304 Not Modified,并没有真的下载数据。你可以看下响应状态码是不是 304,size 显示的是 content size 还是 transfer size。

重点是你这段 fetch 代码有问题:

fetch('/assets/logo.png', {
cache: 'force-cache'
})

虽然 force-cache 看起来是“强制用缓存”,但在实际行为中,浏览器可能会为了“确保新鲜度”而发起网络请求去验证缓存是否过期,尤其是当它不确定的时候。更麻烦的是,fetch 默认不会像 img 标签那样自动带上 If-None-Match 或 If-Modified-Since 去做协商缓存,除非你让它走默认策略。

建议改成这样:

fetch('/assets/logo.png').then(res => res.blob())
.then(blob => URL.createObjectURL(blob));

去掉 cache: 'force-cache',让 fetch 使用默认的缓存策略(和浏览器一样),这样它就会遵循你的 Cache-Control 头,命中强缓存时直接读缓存,没过期就不会发请求。

另外,img 标签和 fetch 的缓存空间不完全共享。img 是由浏览器渲染引擎管理的,fetch 是 JavaScript 层面的资源获取,它们的缓存机制独立。所以你用 img 加载一次,fetch 不一定知道。

还有一个可能:你在测试时用了硬刷新(Ctrl+Shift+R 或者模拟器里的清缓存刷新),这会导致所有资源跳过缓存。移动端调试时记得用普通刷新。

最后一个小建议:可以加上 ETag 或 Last-Modified 配合 Cache-Control,让协商缓存生效,这样即使 max-age 过了也能 304 回来,减少流量。

试试把 fetch 的 cache 选项去掉,然后看下图片请求是不是变成 200 (from disk cache) 或者 304 了。应该就能解决问题了。

希望能帮到你!
点赞 4
2026-02-09 11:24
杏花🍀
你说的这个问题我还真遇到过,移动端缓存确实有些坑。主要问题出在你用 fetch 的地方,它和 img 标签的缓存策略确实不一样。

fetch 默认不会完全依赖 HTTP 缓存,即使你设置了 cache: 'force-cache',也可能会因为浏览器实现细节导致不生效。特别是移动端浏览器,它们对缓存的处理更激进一点。

可以试试这样:把图片加载逻辑改成直接用 Image 标签来处理,避免混用 fetchimg。如果你非要使用 fetch,可以加个额外的缓存验证机制,比如手动检查 res.headers.get('Age') 来判断是否命中了服务端缓存。

另外一个小建议,你的服务端可以再加个 Etag 或者 Last-Modified 头,这样即使 fetch 不走强缓存,也会通过协商缓存减少不必要的下载。

最后附一个改进版代码供参考:
const img = new Image();
img.src = '/assets/logo.png';
document.body.appendChild(img);

// 如果非要 fetch,可以这样优化
fetch('/assets/logo.png', { cache: 'force-cache' })
.then(res => {
if (parseInt(res.headers.get('Age')) > 0) {
console.log('命中了缓存');
} else {
console.log('未命中缓存');
}
return res.blob();
})
.then(blob => URL.createObjectURL(blob));


试一下这个方案,应该能解决你遇到的问题。如果还有其他异常表现,可能得看看具体浏览器版本或者网络环境的影响了。
点赞 13
2026-01-28 19:24