跳到主要内容
极客日志极客日志面向AI+效率的开发者社区
首页博客GitHub 精选镜像AI 生图工具UI配色美学隐私政策关于联系
搜索内容 / 工具 / 仓库 / 镜像...⌘K搜索
注册
博客列表
JavaScriptNode.js大前端

前端多版本零 404 部署实践:根因分析与落地方案

前端发版时旧资源被删导致用户访问 404,核心在于源站清理了旧文件而非浏览器缓存问题。解决方案是在源站保留最近 N 个版本的资源聚合池,配合 HTML 短缓存策略。通过构建产物分离、资源追加同步及入口切换,实现零 404 体验。该方案兼顾回滚效率与缓存命中率,适合高频发版场景。

并发大师发布于 2026/4/10更新于 2026/7/2338 浏览

现象:为什么发布新版本后会出现 404?

一个真实场景:

  • 10:00 用户打开了你的网页(加载的是 v1.0.4 的 HTML)
  • 10:10 你发布了 v1.0.5
  • 用户没有刷新页面,继续点击某个功能
  • 页面尝试按旧 HTML 里的地址加载某个 chunk:/assets/pages-about-about.DK5VADjQ.js
  • 服务器上只剩 v1.0.5 的文件,旧的被删了 → 直接 404

这里的核心逻辑其实很简单:

  • HTML 决定了要加载哪些 JS/CSS(包含具体 hash 文件名)
  • 只要用户手上是旧 HTML,就会请求旧版 hash 的文件
  • 如果服务器把旧文件删了,用户就会 404

根因:不是缓存,而是'源站有没有旧文件'

很多文章只讲'内容哈希 + immutable 缓存',默认隐含'旧文件在源站/存储里还存在'。

  • 有缓存:命中本地/中间缓存,自然不 404
  • 没缓存:会去源站拉,如果源站还保留旧文件,也不会 404
  • 真正的 404 出在'把旧资源从源站删了',而不是'浏览器没缓存'

结论:避免 404 的关键不是浏览器缓存,而是'源站/存储上保留旧文件一段时间'。

方案总览(从易到难)

  • A. 接受小概率 404 → 弹窗提示'有新版本,请刷新'
    • 简单,易落地;体验打断、不优雅
  • B. Service Worker 强制刷新
    • 简化实现;用户会被强制 reload,可能丢失上下文
  • C. 不删旧资源(推荐)
    • 源站/存储保留最近 N 个版本的资源;HTML 短缓存
    • 旧 HTML 始终能拿到对应 js/css,零 404,体验最佳
  • D. 网关/Node 层按 manifest 做 chunk 兜底
    • 复杂度高;适合更重的后端网关治理

这次我们重点聊聊 C 方案。

我们的落地:/assets 聚合 + /versions 备份 + HTML 短缓存

目标很明确:

  • 页面永远从 /assets 加载资源(统一入口,不改路径)
  • 保留最近 3~5 个版本的所有资源在 /assets,旧 HTML 永远能命中
  • 每次发布只切换入口 HTML(dist/current),不动 /assets

目录结构大致如下:

dist/
├─ current/ # 当前入口(index.html、manifest.json 等)
├─ assets/ # 资源聚合池(最近 N 个版本的 js/css/png/…)
└─ versions/ # 版本档案(每次发布的完整备份)
   ├─ v1.0.5/
   │  ├─ index.html
   │  └─ assets/*
   ├─ v1.0.4/
   └─ ...

发布流程(关键规则):

  1. 先 build(产物在 dist/build/h5)
  2. deploy:将 dist/build/h5 复制到 dist/current(并写入 manifest.json)
  3. 同步最近 N 个版本的 assets/* 到 dist/assets(追加,不覆盖已存在同名 hash 文件)
  4. 清理 dist/assets 中'超出 N 版本范围'的多余文件(可控)

为什么这样零 404:

  • 旧 HTML 会请求旧 hash 文件 → dist/assets 中还在 → 命中成功
  • 新 HTML 请求新 hash → 也在 → 命中成功
  • 版本过渡期内(N 版本窗口)不再出现 404

关键实现点(我们做了什么)

  • Vite 输出使用内容哈希:
    • entry/chunk/asset 文件名:assets/[name]-[hash].js|css|…
    • 不再在文件名中带版本号目录(避免同内容不同路径导致缓存失效)
  • 资源聚合器(脚本):
    • 扫描 dist/versions/ 最近 N 个版本的 assets
    • 统一拷贝(或硬链接/软链接)到 dist/assets
    • 清理 dist/assets 中超出窗口的旧文件
  • 入口与资源分离:
    • dist/current 只放 HTML 与轻量入口文件,允许替换
    • dist/assets 仅追加,不随发布清空
  • 预览/线上服务:
    • / → dist/current(HTML 短缓存)
    • /assets/* → dist/assets(long cache + immutable)
    • 可选兜底:未命中再从 dist/build/h5/assets 查找(用于本地预览与排查)

Service Worker 要点(有用但非必须)

  • SW 对 /assets/* 使用'缓存优先 + 网络兜底'或'网络优先 + 缓存兜底',保证 miss 时回源
  • sw.js、index.html 设短缓存(或 no-cache),SW 能及时更新
  • 若暂不使用 SW,本方案也能零 404;SW 仅作为进一步优化

为什么很多文章不强调'多版本零 404'?

  • 默认'旧文件不删':使用 OSS/CDN/对象存储,资源是追加上传,旧对象长期存在
  • 痛点阈值:发版不频繁、用户会刷新、会话短;小概率 404 用'刷新提示'即可
  • 文档聚焦'原则':内容哈希 + immutable + HTML 短缓存,被当作默认前置

当你遇到高频发版、长会话、动态导入较多、不能打断用户的场景时,就必须系统性解决'过渡期 404'。

其他常见做法(简短说法,便于对比)

  • 对象存储/CDN 是追加写:
    • 带 hash 的静态资源上传到固定前缀,永不覆盖、不清理
    • 每次发版只上传新增;HTML 短缓存可覆盖
  • 原子部署但'只切入口,不清资源':
    • /releases/<time_hash>,/current 指向最新 release;assets 不删
    • 回滚只切回旧 /current
  • Web 服务器层'只换指针,不清资产':
    • Nginx/OpenResty:/ alias 到当前 HTML 目录;/assets alias 到公共资产池
  • 构建/同步策略'追加而非覆盖':
    • rsync/脚本:跳过已存在同名文件,只追加;不执行 rm -rf assets
  • CDN 层'天然长留':
    • 源站是 OSS,CDN 默认不删历史对象;只刷新 HTML

一句话:避免 404 依赖'源站/存储保留旧文件',不是依赖'浏览器缓存'。

我们的流程清单

  • 统一路径:页面永远从 /assets 加载资源
  • 发布顺序:build → deploy:sync(聚合) → 切换 current
  • 保留策略:versions 保留 10 个档案;assets 保留最近 3~5 个版本资源
  • 清理策略:仅清理超出窗口的文件;不清空 assets 目录
  • 观察/回滚:问题时只切换 HTML(current),资源无需变动
    • (可选)SW:/assets 有兜底;HTML/ sw.js 短缓存

FAQ(你可能会问)

  • Q:为什么不用把版本号加进路径(如 /v1.0.5/assets/…)?
    • A:这样会让相同内容的 URL 不同,缓存无法复用。内容哈希已能区分新旧;版本应体现在'保留与聚合'的策略,不应体现在资源 URL。
  • Q:把聚合池直接放到 dist/build/h5/assets 可以吗?
    • A:可以,但构建时常会清空该目录,易被覆盖。更稳妥是用 dist/assets 做聚合池;若坚持放 build 目录,务必在 build 之后再做聚合,并避免之后再执行 build。
  • Q:磁盘会不会涨?
    • A:带 hash 的资源相同内容只存一份;保留 3~5 个版本通常增长有限。也可配置自动清理超出窗口的文件。

总结

  • 真因:404 不是'没缓存',而是'源站删了旧文件'
  • 原则:内容哈希 + immutable + HTML 短缓存
  • 方法:/assets 聚合最近 N 个版本资源 + /versions 备份入口
  • 效果:零 404、缓存最优、秒级回滚、可观测、易维护

当发布频繁、会话长、需要极致稳定体验时,这套工程化方案能显著提升质量与口碑。

目录

  1. 现象:为什么发布新版本后会出现 404?
  2. 根因:不是缓存,而是“源站有没有旧文件”
  3. 方案总览(从易到难)
  4. 我们的落地:/assets 聚合 + /versions 备份 + HTML 短缓存
  5. 关键实现点(我们做了什么)
  6. Service Worker 要点(有用但非必须)
  7. 为什么很多文章不强调“多版本零 404”?
  8. 其他常见做法(简短说法,便于对比)
  9. 我们的流程清单
  10. FAQ(你可能会问)
  11. 总结
  • 免费图片AI生成工具免费生成了解详情
  • Magick API 一键接入全球大模型注册送1000万token查看
  • 免费图片视频在线生成30秒,将你的创意变成现实开始设计
  • X/Twitter免费视频下载器免登陆无限额度免费视频解析下载了解详情
  • 100+免费在线小游戏爽一把
极客日志微信公众号二维码

微信扫一扫,关注极客日志

微信公众号「极客日志V2」,在微信中扫描左侧二维码关注。展示文案:极客日志V2 zeeklog

更多推荐文章

查看全部
  • 基于 OneDNS 的高校办公网安全防护方案
  • Faster-Whisper 在笔记本 CPU 环境下如何选择模型模式
  • C++ 递归实战:合并有序链表与反转链表
  • 基于 YOLO12 的无人机航拍视角目标检测系统
  • 位运算实战:判断字符唯一性与查找丢失数字
  • 前端 HTML 转 PDF 的两种主流方案深度解析
  • 基于 SpringBoot 的图书租借系统设计与实现
  • AI 对话应用接口开发:同步、SSE 流式与智能体前端对接
  • SpiffWorkflow:纯 Python 实现的工作流引擎
  • SKResNet 架构详解:融合选择性卷积与残差结构
  • OpenClaw 底层原理深度解析:本地优先的任务执行系统
  • AnythingLLM:零成本搭建私人 ChatGPT,支持主流大模型
  • Visual Studio 使用 GitHub Copilot 与 IntelliCode 辅助编码
  • 前端常用加密方式与算法解析
  • Flutter 底部导航与 TabBar 多页切换实战及状态保持
  • OpenClaw 多飞书机器人绑定配置实战指南
  • 实战 LLaMA Factory:在国产 DCU 上高效微调 Llama 3 模型
  • STL 文件预览工具:使用 stl-thumb 生成 3D 模型缩略图
  • 无需公网 IP 安全远程访问本地 AI 服务方案
  • 纯 HTML 实现的交互式星球漫游效果

相关免费在线工具

  • Keycode 信息

    查找任何按下的键的javascript键代码、代码、位置和修饰符。 在线工具,Keycode 信息在线工具,online

  • Escape 与 Native 编解码

    JavaScript 字符串转义/反转义;Java 风格 \uXXXX(Native2Ascii)编码与解码。 在线工具,Escape 与 Native 编解码在线工具,online

  • JavaScript / HTML 格式化

    使用 Prettier 在浏览器内格式化 JavaScript 或 HTML 片段。 在线工具,JavaScript / HTML 格式化在线工具,online

  • JavaScript 压缩与混淆

    Terser 压缩、变量名混淆,或 javascript-obfuscator 高强度混淆(体积会增大)。 在线工具,JavaScript 压缩与混淆在线工具,online

  • Base64 字符串编码/解码

    将字符串编码和解码为其 Base64 格式表示形式即可。 在线工具,Base64 字符串编码/解码在线工具,online

  • Base64 文件转换器

    将字符串、文件或图像转换为其 Base64 表示形式。 在线工具,Base64 文件转换器在线工具,online