页面突然只剩 DOM?一次静态资源版本错配排查

文章目录

页面突然只剩 DOM?一次静态资源版本错配排查

前几天,我给项目发布了一个新版本。部署过程没有报错,自己访问也很正常,原以为这次上线已经结束。

没过多久,有用户发来一张截图:页面里的文字和按钮都还在,但布局、颜色和交互全没了。整个网站像是浏览器关闭了 CSS,只剩下一堆原始的 DOM 元素。

故障示意图:CSS 和 JavaScript 静态资源请求返回 404,页面因此只剩下未经样式修饰的 DOM 元素。

页面还在,样式为什么没了

遇到这种情况,我的第一反应是打开浏览器开发者工具。

在 Network 面板中过滤 CSS 和 JS 请求,很快就能看到几个红色的 404。控制台里通常也会出现类似错误:

text 复制代码
Failed to load resource: the server responded with a status of 404
ChunkLoadError: Loading chunk xxx failed

页面的 HTML 已经成功返回,所以文字、按钮和输入框还在。负责样式和交互的静态资源没有加载成功,页面自然就只剩下最原始的样子。

真正的问题出在版本发布与缓存之间。

一次版本错配是怎么发生的

前端项目构建后,文件名通常会带上内容哈希:

text 复制代码
app.a81f3c.css
vendor.71bd29.js
main.c8e602.js

只要文件内容发生变化,哈希也会跟着变化。下一次构建可能得到另一组文件:

text 复制代码
app.591de8.css
vendor.71bd29.js
main.e320a1.js

这套机制本身没有问题。哈希能让浏览器长期缓存静态资源,又能在文件发生变化时请求新地址。

麻烦出在旧版本被过早删除。

用户访问网站后,HTML 可能缓存在浏览器、本地代理或 CDN 节点中。发布新版本时,如果部署脚本直接清空旧目录,再上传本次构建产物,旧文件 app.a81f3c.css 就不存在了。

这时,不同用户拿到的内容可能并不一致:

text 复制代码
浏览器或 CDN 中的旧 HTML
            ↓
请求 app.a81f3c.css
            ↓
服务器只保留 app.591de8.css
            ↓
返回 404
            ↓
页面只剩 DOM 元素

换句话说,缓存没有失效并不可怕。真正导致故障的是:旧 HTML 仍然可以访问,它依赖的旧静态资源却已经被删除。

这也解释了为什么问题经常很难复现。刚发布版本的开发者可能拿到了最新 HTML,页面一切正常;某些用户命中了旧缓存,看到的却是一片没有样式的页面。

我的处理方法:让新旧版本共存

我的解决思路是保留一段时间内的静态资源版本,而不是每次发布都覆盖同一个目录。

静态资源可以放在 OSS 的版本目录中,也可以保存在前端服务器的挂载卷里。例如:

text 复制代码
/releases
├── 2026-08-12
│   └── assets
│       ├── app.a81f3c.css
│       └── main.c8e602.js
└── 2026-08-13
    └── assets
        ├── app.591de8.css
        └── main.e320a1.js

发布后,新旧资源都能通过原来的 URL 访问:

text 复制代码
https://static.example.com/releases/2026-08-12/assets/app.a81f3c.css
https://static.example.com/releases/2026-08-13/assets/app.591de8.css

用户命中旧 HTML 时,继续加载旧版本资源;缓存过期并重新获取 HTML 后,再自然切换到新版本。整个过程不需要强制用户刷新,也不会出现 HTML 与静态资源对不上的中间状态。

发布顺序也要调整

保留旧资源只能解决一部分问题,发布顺序同样重要。我现在采用下面的流程:

  1. 上传新版本的 CSS、JavaScript、字体和图片。
  2. 检查新静态资源是否能够通过公网地址访问。
  3. 发布引用这些资源的新 HTML。
  4. 等待缓存周期结束后,再清理过期版本。

新资源必须先于 HTML 上线。否则用户可能已经拿到新 HTML,服务器上却还没有对应的 CSS 和 JavaScript,结果依然是 404。

旧版本也不需要永久保存。保留时间可以按最长缓存时间计算,再增加一点缓冲。例如 CDN 最长缓存 7 天,可以将旧资源保留 10 至 14 天。具体时间取决于项目的发布频率、回滚要求和存储成本。

缓存策略需要分开设置

HTML 和带哈希的静态资源不适合使用同一套缓存策略。

带哈希的文件内容不会发生变化,可以长期缓存:

nginx 复制代码
location ~* \.(css|js|png|jpg|svg|woff2)$ {
    expires 1y;
    add_header Cache-Control "public, max-age=31536000, immutable";
}

HTML 决定当前页面引用哪个版本的资源,缓存时间应该短一些,也可以要求客户端重新验证:

nginx 复制代码
location = /index.html {
    add_header Cache-Control "no-cache";
}

如果项目是单页应用,还要检查路由回退后的 HTML 是否带有正确的缓存响应头。项目使用 Service Worker 时,也要确认它有没有继续返回更早的 HTML。

怎样快速确认是同一个问题

下次再遇到"页面只有文字,没有样式",可以直接检查下面几项:

  • Network 面板中的 CSS、JavaScript 是否返回 404。
  • 请求地址里的哈希文件是否存在于 OSS 或服务器。
  • 当前 HTML 引用的是哪个发布版本。
  • CDN、浏览器或 Service Worker 是否仍在缓存旧 HTML。

如果旧 HTML 请求的资源已经被部署脚本删除,原因基本就找到了。

写在最后

这次问题表面上像是 CSS 突然失效,实际是发布系统没有照顾仍在缓存中的旧页面。

我后来把部署方式改成了版本化存储:先上传新资源,再发布 HTML,旧资源等缓存过期后再清理。改动不复杂,却消除了一个很隐蔽的发布窗口。

用户什么时候刷新页面,我们控制不了。但只要旧页面依赖的文件还在,它就不至于突然"裸奔"。

相关推荐
Csvn18 小时前
WASM
前端
修己xj19 小时前
我用 AI 给自己写了一个 Markdown 编辑器,然后把它开源了
前端
FRing0319 小时前
vue2干货总结2(v-bind、v-for、v-model、指令修饰符、v-bind操作class和style)
开发语言·前端·vue.js·vue2
幸运小猿20 小时前
模拟慢上传-处理超时重试image 丢失线上问题
前端·javascript·html
moshuying21 小时前
网购能买Token了!
前端
h_654321021 小时前
echarts实现柱状图加一条横线
前端·echarts
风骏时光牛马21 小时前
生成一个后端标题,只需要一个标题,不需要其他的任何文本。
前端
子非鱼@Itfuture21 小时前
关于前后端联调中登录状态/登录信息失效?
java·前端
郭小李21 小时前
openFeign入门
java·开发语言·前端
馒头不想说话1 天前
AI当运营,帮你省人力
前端