为什么改完代码,页面却没变?
一次很小的样式更新,构建正常,部署正常,本地验证也正常。但打开线上页面,按钮还是原来的样子。刷新几次没有变化,直到清空浏览器缓存,一切才终于对上。
问题不在 CSS,而在资源的生命周期:文件内容更新了,URL 没变,浏览器仍然认为手里的副本是新鲜的。于是,一次发布是否生效,变成了用户有没有碰巧刷新缓存。
先把缓存的边界画清楚
一次资源请求,通常会依次经过浏览器缓存、CDN 边缘节点,最后才到源站。每一层都可能直接返回副本,也都需要一套清晰的更新约定。
图 01 · 前一层无法满足请求时,才继续向后查找。
这里先聚焦两类文件:作为入口的 HTML,以及由 HTML 引用的脚本、样式和图片。入口负责告诉浏览器「当前版本是什么」,其余资源负责提供这一版本的具体内容。
| 资源类型 | 建议策略 | 更新方式 |
|---|---|---|
| HTML 入口 | no-cache | 使用前重新验证 |
| 带指纹的 JS / CSS | 长期缓存 + immutable | 内容变化时生成新 URL |
| 固定地址的图片 | 短期缓存或重新验证 | 按实际更新频率调整 |
no-cache 并不等于不允许存储。它允许保存副本,但再次使用前需要验证;资源未变时,服务端可以返回 304,省去重复传输响应体。
让文件名承担版本管理
为构建产物加入内容哈希,是最直接的一步。比如 app.8f31c2.js:只要内容不变,URL 就不变;内容一旦变化,就生成新的地址。
把长期缓存限定在带指纹的目录
下面的 Nginx 片段放在现有 server 配置中。约定 /assets/ 只包含构建生成、带内容指纹的文件;固定文件名的资源需要单独配置。
# HTML 入口:每次使用前重新验证
location = /index.html {
add_header Cache-Control "no-cache";
}
# 仅用于带内容指纹的构建资源
location /assets/ {
try_files $uri =404;
add_header Cache-Control "public, max-age=31536000, immutable";
}发布时先上传新资源,再更新 HTML 入口。旧资源保留一段时间,让仍打开旧页面的用户可以继续访问对应的文件,也为回滚留出余地。
发布前,再检查一遍
验证缓存策略,不需要依赖「多刷新几次」。打开开发者工具,观察 Network 面板中的响应头和资源地址,就能确认大部分问题。
- 检查入口:确认 HTML 的
Cache-Control与预期一致,重新验证机制正常。 - 检查产物:修改一行代码后重新构建,确认对应文件的哈希发生变化。
- 检查发布顺序:新入口引用的文件都已上传,旧版本资源仍然可用。
- 模拟旧页面:保留一个旧标签页,在新版本发布后继续操作,观察是否出现资源 404。
缓存的目标,是让重复请求更便宜,也让更新可靠到达。把入口和资源分开管理之后,「用户看到哪个版本」就有了明确的答案。
