资源不同步本质是缓存策略与资源标识未对齐,核心解法是确保URL唯一对应内容;background-image不更新因强缓存下URL未变,需用contenthash、版本参数或immutable头实现精准缓存控制。

资源不同步的本质是浏览器用了旧缓存,而服务器已部署新文件——这不是“没刷新”,而是缓存策略和资源标识没对齐。核心解法就一条:让每个资源的 URL 唯一对应其内容,否则浏览器分不清“同个 URL 的新旧版本”。
为什么 background-image 换图后页面不更新
浏览器把 url(https://cdn.example.com/avatar.jpg) 当作唯一键来查缓存。S3 或 Nginx 返回了 Cache-Control: max-age=31536000,那这个 URL 就会被强缓存一年。你替换了 S3 上的 avatar.jpg 文件,但 URL 没变,浏览器根本不会重请求。
常见错误操作:
- 只改了图片文件,没改 HTML/CSS 里的 URL
- 加了
?v=2但忘了同步构建流程,导致 HTML 引用的是avatar.jpg?v=2,而服务器上只有avatar.jpg?v=1 - 用 Webpack 打包却关掉了
contenthash,JS/CSS 文件名不变,哈希没注入到 HTML 中
Cache-Control 头怎么配才不冲突
静态资源(CSS/JS/图片)和 HTML 必须反着设:前者要长缓存 + 不可变,后者要短缓存或协商验证。否则就会出现“HTML 更新了,但引用的 app.a1b2c3.js 还是旧的”或反过来。
立即学习“前端免费学习笔记(深入)”;
推荐配置:
将调研报告、方案对比、数据分析、需求梳理、会议纪要、设计说明、代码评审、学习笔记等任务产物输出为可在浏览器直接打开的单文件 HTML,替代 Markdown 作为最终交付物。Markdown 在信息密度、视觉清晰度和可分享性上已跟不上 AI 产物的丰富度,HTML 能同时承载排版、表格、SVG 等。
- CSS/JS/字体/图片:响应头设
Cache-Control: public, max-age=31536000, immutable——immutable告诉浏览器“这资源永不变更”,允许跳过后续If-None-Match请求 - HTML 文件(尤其是 SPA 的
index.html):设Cache-Control: no-cache, must-revalidate,或更稳妥的max-age=0, must-revalidate;避免用no-store,它会杀死所有缓存收益 - API 接口:默认不缓存,显式加
Cache-Control: no-store或private, no-cache
Service Worker 怎么不变成缓存黑洞
一旦注册了 SW,它就接管所有 fetch 请求。如果缓存逻辑写死、没做版本清理,用户永远卡在旧版 HTML + 旧版 JS 的组合里,连硬刷新(Ctrl+F5)都无效。
关键实操点:
- 每次更新 SW 脚本内容后,必须改脚本文件名或加哈希(如
/sw.20261010.js),否则浏览器认为“没变化”,不会触发 install -
cache.addAll()列表里必须包含 HTML 所依赖的全部资源路径,且全部用绝对路径(/index.html✅,index.html❌);漏一个,离线时整个缓存就空转 - 在
fetch事件里按event.request.destination分流:对script和style用 cache-first,对document用 stale-while-revalidate,对json或form用 network-only
CDN 和代理层怎么绕过缓存陷阱
Cloudflare、阿里云 DCDN、Nginx 反向代理这些中间层,常常比浏览器还“固执”。它们可能把 /index.html 当作静态资源缓存了 24 小时,你清了浏览器缓存也没用。
排查与修复步骤:
- 用
curl -I https://yoursite.com/index.html看真实响应头,重点检查Cache-Control、Expires、Age字段 - CDN 后台单独为
/index.html和/manifest.json设置缓存 TTL = 0 或 “不缓存”;不要让它继承全局静态规则 - Nginx 配置中,对 HTML 文件显式覆盖:
location = /index.html { add_header Cache-Control "no-cache, must-revalidate"; expires -1; } - 如果用了 Server-Side Rendering(如 Next.js、Nuxt),确保服务端输出的 HTML 响应头没被中间层覆盖——有些 CDN 默认强制加
public, max-age=3600
最常被忽略的一点:构建产物哈希是否真正注入到了所有引用处。Webpack 的 [contenthash]、Vite 的 __STATIC_CONTENT_HASH__ 替换,必须同时作用于文件名和 HTML 中的 href/src 属性。少一处,就断一层同步链路。


















