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

文章目录

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

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

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

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

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

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

在 Network 面板中过滤 CSSJS 请求,很快就能看到几个红色的 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,旧资源等缓存过期后再清理。改动不复杂,却消除了一个很隐蔽的发布窗口。

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

相关推荐
东风破_1 小时前
ESLint 是什么?为什么你的项目需要它?
前端·后端·代码规范
圣殿骑士-Khtangc3 小时前
Go字符串高效拼接性能对比与底层原理分析
服务器·前端·golang
BigTopOne4 小时前
【ijkplayer】 硬解码流程
前端
Kendra9194 小时前
从 0 到上线:单文件网页 App 部署到阿里云 ECS 全流程(Nginx+Node+PM2+PostgreSQL)
nginx·阿里云·云计算·部署·个人开发·今天吃什么
kyriewen4 小时前
DeepSeek Harness开源第一天我就上手了——和Claude Code的差距比想象中大
前端·ai编程·deepseek
捡田螺的小男孩4 小时前
什么是 Skill?手把手带你写一个简单有用的 Skill!
前端·后端·程序员
IT_陈寒5 小时前
Redis集群这个坑,差点让我通宵
前端·人工智能·后端
90后的晨仔5 小时前
uni-app 路由跳转与页面导航 —— 终极详解(对标 iOS / Android / 鸿蒙三端原生)
前端
Heo5 小时前
大厂前端调试不能只会debugger
前端·javascript·面试