问题:更新后用户仍加载旧版静态资源
故障定位思路
Single Page看似简单,真正落地时却很容易踩坑。部署新版本后,用户浏览器因强缓存仍引用旧的 .js、.css 文件,导致页面白屏或功能异常。这是 SPA 最常见的发布事故之一。根本原因在于:路由器或 HTML 文件动态加载的静态资源路径未随版本变化,而服务器设置了过长的 Cache-Control: max-age。
核心方案:文件指纹 + 强缓存 + 增量部署
1. 文件指纹:让文件名随内容改变
使用 Webpack / Vite 的 contenthash(内容哈希)。当源码变化时,生成的资源文件名不同:app.a3b2c1.js → app.d4e5f6.js。HTML(或 entry 文件)中引用的路径自然更新,旧文件被浏览器忽略。
- Webpack:
output.filename = '[name].[contenthash:8].js' - Vite:默认已启用
build.rollupOptions.output.entryFileNames中的哈希 - 注意:避免使用
chunkhash(依赖 chunk 包边界)或hash(整个构建一次),保证缓存颗粒度最细。
2. CDN 强缓存配置
对带有指纹的静态资源,在源站或 CDN 上设置 Cache-Control: public, max-age=31536000, immutable。一年期的强缓存,让用户永远不请求这些文件,直接使用缓存。关键前提:文件名变化时,旧文件必须可被安全删除或保留(推荐保留以备回滚)。
对于不包含指纹的 HTML 文件(如 index.html),设置 Cache-Control: no-cache 或 must-revalidate,确保每次请求都回源校验。
实施细节
3. 构建输出与部署流程
- 构建目录结构:
dist/
├── index.html
├── js/ (文件名含 hash)
├── css/
└── assets/ - 上传至 CDN 或对象存储时,保留所有历史版本文件(例如使用目录版本或唯一文件名),避免回滚时 404。
- 部署脚本:将新
index.html与静态资源同时推送,先推送静态资源(预热 CDN),再推送 HTML。
4. CDN 缓存刷新与预热
即使强缓存过期时间设为一年,在紧急修复时仍需强制刷新:
- 精准刷新:仅刷新
index.html,因为静态资源文件名已变,旧 URL 不再被引用。 - 批量刷新:如果必须同时更新旧文件名(如 vendor chunk 拆分不合理),刷新整个目录或使用通配符。
- 预热:部署后立即请求新静态资源 URL,触发 CDN 回源缓存,避免首次用户慢速。
想继续深入:此处可内链到“Single Page优化清单”文章。
进阶阅读:此处可内链到“Single Page性能优化”指南。
关联教程:此处可内链到“Single Page部署与验证”内容。
特殊场景处理
5. Service Worker 缓存干预
如果 SPA 使用 Service Worker 进行离线缓存,必须协调 SW 缓存策略:
- SW 脚本本身使用
Cache-Control: no-cache,保证每次更新。 - SW 中采用
cache-first配合版本号(在 SW install 事件中清除旧缓存)。 - 定期向 CDN 请求
sw.js的字节范围以触发更新(部分 CDN 支持)。
6. 回滚操作
当新版本出现严重 Bug 时,若旧版本静态资源仍在 CDN 上(未被 purge),只需回滚 index.html 到旧版本。HTML 中引用的是旧指纹资源路径,用户浏览器中这些资源因强缓存仍在本地,立即可用。若旧资源已被删除,则需从备份恢复或重新构建旧版本。
验证与监控
容易忽略的细节
- 发布后使用 cURL 检查
index.html的Cache-Control头是否为no-cache。 - 检查 JS/CSS 资源的
Cache-Control是否为max-age=31536000及immutable。 - 通过浏览器 DevTools 的 Network 面板确认新版本资源是否被缓存命中(Size 列显示 memory cache / disk cache)。
- 在 CDN 控制台观察回源日志,确保静态资源回源率低于 1%。
相关阅读:此处可内链到“Single Page常见问题”专题。
Single Page:常见误区
我的处理经验
- 过度依赖 ETag/Last-Modified:它们仍会产生条件请求(304),不如强缓存零请求。
- 在 URL 末尾加 query string 版本号:部分 CDN 和浏览器不会缓存带 query 的资源,且修改 query 不触发某些代理缓存刷新。
- 忘记处理 CSS 中的字体、图片引用:这些资源也应使用 content hash 命名,否则可能因旧缓存而显示异常。
- 强缓存时间太短:
max-age=86400会导致每天用户重新请求,浪费带宽。
总结——Single Page
先看关键判断
SPA 静态资源版本控制与 CDN 强缓存并非对立面,而是互补。核心逻辑:带指纹的资源永久强缓存,不带指纹的入口文件禁止缓存。合理配置构建工具、部署顺序、CDN 刷新策略,即可平衡首屏性能与更新及时性。对于高可用要求的站点,建议保留至少两版旧资源,同时结合 Service Worker 的版本管理,实现无缝升级。把这些步骤跑通后,Single Page基本就能稳定落地。
延伸阅读
