前端多版本零404部署实践:为什么会404,以及怎么彻底解决

这是一篇给“小白也能看懂”的实践文:讲清现象、根因、方案选择与我们的落地实现。

1. 现象:为什么发布新版本后会出现 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

2. 根因:不是缓存,而是“源站有没有旧文件”

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

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

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


3. 方案总览(从易到难)

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

本文主讲 C 方案。


4. 我们的落地:/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

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

  • 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 查找(用于本地预览与排查)

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

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

7. 为什么很多文章不强调“多版本零404”?

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

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


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

  • 对象存储/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 依赖“源站/存储保留旧文件”,不是依赖“浏览器缓存”。


9. 我们的流程清单

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

10. 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 个版本通常增长有限。也可配置自动清理超出窗口的文件。

11. 总结

  • 真因:404 不是“没缓存”,而是“源站删了旧文件”
  • 原则:内容哈希 + immutable + HTML 短缓存
  • 方法:/assets 聚合最近 N 个版本资源 + /versions 备份入口
  • 效果:零 404、缓存最优、秒级回滚、可观测、易维护
当发布频繁、会话长、需要极致稳定体验时,这套工程化方案能显著提升质量与口碑。

Read more

揭秘Docker资源清理难题:如何一键停止并删除所有容器(含实战命令)

第一章:Docker资源清理的背景与挑战 在现代云原生开发环境中,Docker作为容器化技术的核心工具,被广泛用于应用的构建、分发与运行。随着频繁的镜像构建和容器启动,系统中会积累大量未使用的资源,包括停止的容器、孤立的网络、废弃的卷以及悬空镜像。这些“残留”资源不仅占用磁盘空间,还可能影响系统性能与安全策略的执行。 资源积压的典型来源 * 悬空镜像(dangling images):构建过程中产生的中间层或标签丢失的镜像 * 已停止的容器:运行后退出但未被删除的容器实例 * 未被挂载的卷(volumes):容器删除后遗留的数据卷,可能包含敏感信息 * 孤立网络:自定义网络在服务移除后未被清理 Docker内置清理命令示例 # 清理所有未使用的资源(容器、网络、镜像、构建缓存) docker system prune -a # 仅清理悬空镜像 docker image prune # 强制清理,不提示确认 docker system prune -af 上述命令通过Docker守护进程识别并移除无引用资源,适用于常规维护。

By Ne0inhk
Flutter 三方库 cli_script 鸿蒙化极简命令行执行引擎适配探索:在多维沙盒终端环境注入异构 Shell 串联逻辑彻底拔高全自动化容器脚本运维及-适配鸿蒙 HarmonyOS ohos

Flutter 三方库 cli_script 鸿蒙化极简命令行执行引擎适配探索:在多维沙盒终端环境注入异构 Shell 串联逻辑彻底拔高全自动化容器脚本运维及-适配鸿蒙 HarmonyOS ohos

欢迎加入开源鸿蒙跨平台社区:https://openharmonycrossplatform.ZEEKLOG.net Flutter 三方库 cli_script 鸿蒙化极简命令行执行引擎适配探索:在多维沙盒终端环境注入异构 Shell 串联逻辑彻底拔高全自动化容器脚本运维及工程交付效率 在跨平台开发、构建流自动化以及服务器端管理中,如何简洁地调用命令行工具并处理其输入输出是提升效率的关键。cli_script 是一个将 Dart 现代语法与 Shell 脚本简洁性完美结合的库。本文将探讨该库在 OpenHarmony 开发工作流中的适配与应用。 前言 什么是 cli_script?它不是一个运行在手机端的 UI 库,而是一个面向开发者的命令行辅助库。它让你可以用 Dart 编写像 Bash 一样简洁但在处理异常和异步流上更强大的脚本。在鸿蒙这个拥有独立命令行工具(hdc, ohpm)的开发生态中,利用 cli_script 可以极速构建自定义的鸿蒙构建、测试和部署流水线。 一、原理解析

By Ne0inhk
【OpenClaw从入门到精通】第05篇:实战!OpenClaw自动化搞定邮件/日历/文件管理(2026实测避坑版)

【OpenClaw从入门到精通】第05篇:实战!OpenClaw自动化搞定邮件/日历/文件管理(2026实测避坑版)

摘要:本文聚焦OpenClaw核心实用场景,手把手教你实现邮件、日历、文件管理的全自动化。通过安装三大核心技能,配置IMAP/SMTP邮箱、跨平台日历及文件沙盒权限,结合4个虚拟实战案例,演示从邮件自动摘要、会议冲突预警到下载文件夹分类的完整流程。文中包含2026实测的命令代码、配置步骤、排障方案,所有案例需在测试环境验证,代码未上传GitHub。兼顾新手入门与进阶需求,让OpenClaw从“能聊天”升级为“能干活”,帮你节省80%重复工作时间。 优质专栏欢迎订阅! 【DeepSeek深度应用】【Python高阶开发:AI自动化与数据工程实战】【YOLOv11工业级实战】 【机器视觉:C# + HALCON】【大模型微调实战:平民级微调技术全解】 【人工智能之深度学习】【AI 赋能:Python 人工智能应用实战】【数字孪生与仿真技术实战指南】 【AI工程化落地与YOLOv8/v9实战】【C#工业上位机高级应用:高并发通信+性能优化】 【Java生产级避坑指南:高并发+性能调优终极实战】【Coze搞钱实战:

By Ne0inhk

JMeter+Jenkins+AI自动化测试

一套“JMeter+Jenkins+AI”的自动化测试解决方案,核心是攻克JMeter脚本开发中的参数化、关联、断言三大核心技能,同时借助AI实现测试用例自动生成、脚本冗余优化、报错快速定位,以此打破重复劳作的困局,大幅提升测试效率和职场竞争力。 一、夯实JMeter核心技能(企业级常用方案) 这是自动化的基础,先掌握参数化、关联、断言的企业级实现方式,也是AI赋能的前提。 1. 多场景参数化:用户定义变量 + CSV Data Set Config 企业中常需测试不同用户/场景,用CSV管理测试数据,配合用户定义变量提升脚本灵活性,彻底摆脱硬编码。 实操步骤: ① 准备CSV测试数据文件(test_data.csv,放在JMeter脚本同级目录): username,password,expected_code,scenario user01,123456,200,正常登录 user02,wrongpwd,

By Ne0inhk