页面突然只剩 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,旧资源等缓存过期后再清理。改动不复杂,却消除了一个很隐蔽的发布窗口。

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

相关推荐
jerrywus8 小时前
让 AI 写完代码后交一份报告:我改造了 answer-me-with-html
前端·claude·cursor
派小心.8 小时前
小程序分享链路中来源参数的传递校验
大数据·前端·数据库·人工智能·小程序·数据分析
hhb_6189 小时前
一键生成数据看板,ECharts实战演示
前端·javascript·echarts
Java后端的Ai之路9 小时前
08_React组件API
前端·react.js·前端框架
小马不起床9 小时前
零基础玩转 FastAPI_09:前端基础工程构建
前端·状态模式·fastapi
风骏时光牛马10 小时前
用户管理模块接口开发
前端
彭亚川Allen10 小时前
从零手搓 RAG 之检索全流程,给大模型接上自己的知识库
前端·后端·ai编程
IT_陈寒10 小时前
别让Java字符串拼接偷偷吃掉你的内存
前端·人工智能·后端
软件技术NINI11 小时前
圣罗兰彩妆html+css 4页
前端·css·html
2601_9516152011 小时前
网站模板 网站网页模板下载
前端·网络·html