---
title: "测试环境 APK 按钮离奇失踪：半小时把锅从 Nuxt 甩回 OSS"
date: 2026-02-17
description: "Nuxt 3 静态下载页：App Store、Google Play 都正常，唯独 Direct APK 在测试环境稳定蒸发；本地和生产却没事。复盘如何用 Cursor Agent 半小时串起页面路由、JS chunk、hash 资源与 OSS Key，锁定\"构建引用与部署资源不一致\"——以及 Jenkins 全绿为何照样翻车。"
canonical_url: "https://yijinlee.com/articles/article-39"
tags:
  - "Nuxt 3"
  - 静态部署
  - OSS
  - CDN
  - Vite
  - 排障
  - "Cursor Agent"
topic: "AI 外骨骼"
---

下午，同事顶着黑眼圈晃到我工位："帮看看这个诡异问题。"

某个 Nuxt 3 静态站的移动端下载页：App Store 正常，Google Play 正常，**唯独 "Direct APK Download" 按钮神秘消失**。

更离谱的是环境分布——

| 环境 | APK 按钮 
| 本地 dev | ✅ 有 |
| 生产 | ✅ 有 |
| Jenkins | ✅ 全绿 |
| 测试 | ❌ 稳定复现 |

一时间怀疑人生：Nuxt 编译抽风？静态资源漏打包？CDN 缓存下毒？

这种"只在某个环境出现"的 Bug，最折磨人。我们这次让 Cursor Agent 跟着走了一遍排查，**半小时把证据链串死了**——结论先说：**不是 AI 自动修好的，是 AI 帮咱们少走了很多弯路。**

---

#### 一、案发现场：三缺一，缺得很有纪律

> "我明明手动上传了 apk.png 啊！OSS 里有的！"

（别急，这句话后面会回旋镖。）

页面现象很整齐：两个商店按钮在，APK 不在。不是偶发，是**测试环境稳定复现**。

🕵️
- Nuxt 编译是不是抽风了？
- 静态资源是不是漏打进包里？
- CDN 缓存是不是有毒？
- 是不是测试环境根本没部署最新构建？

传统排查的话，这套流程能折腾一整天：全局搜 apk、本地 generate、diff .output、curl HTML、登 OSS、翻 Jenkins……前端和运维容易互相甩锅。

---

#### 二、先别在错的文件里加班

同事一开始盯的是 `components/download/LegacyPanel.vue`。

Agent 在仓库里语义搜了一圈：**截图里的页面其实是 `/download`**，对应 `pages/download/index.vue`。

而且 APK 按钮是**直接渲染**的——没有 `v-if`，没有权限分支，没有"仅 iOS 显示"那套。

💡 **这一步值半小时**：很多线上问题，修只要五分钟，**找错文件能找五小时**。

---

#### 三、代码侧：Vue 无罪

顺着真页面往下看，逻辑很直白：三个下载入口并排，APK 没被谁藏起来。

所以"按钮被条件渲染吃掉"这条线，可以当场划掉。

---

#### 四、构建侧：本地 generate 也没翻车

本地跑 `npm run generate`，翻 `.output/public/_nuxt/`：

构建产物里**有** `apk.a1b2c3d4.png`——图打进包了，hash 也正常。

于是再划掉一批经典甩锅：

- 代码没提交
- 资源没打包
- Vue 构建异常

问题开始往 **"部署之后，运行时拿到的资源不对"** 收敛。

---

#### 五、线上取证：curl 不会跟你客气

接下来是整案最关键的一步：**别猜，去拉测试环境和生产的 HTML、JS chunk。**

```bash
# 示意：拉页面引用的 chunk，搜 apk 相关字符串
curl -s "https://test.example.com/download" | grep -o '_nuxt/[^"]*\.js'
curl -s "https://test.example.com/_nuxt/xxxxx.js" | grep -i "apk"
```

对比结果很干脆：

| 对比项 | 测试环境 | 生产环境 |
| --- | --- | --- |
| chunk 里是否有 demo-app.apk 相关引用 | ❌ 没有 | ✅ 有 |
| 推断 | 跑的像旧构建或未完整部署 | 与当前构建一致 |

🔍 **测试环境加载的 JS 里根本没有 apk 相关引用**——说明浏览器执行的并不是你以为的那份新包。

到这里，锅已经不太像"前端写错一行"了。

---

#### 六、OSS 截图一锤定音

同事发来 OSS 控制台：**NoSuchKey**。

请求路径：

`/_nuxt/apk.a1b2c3d4.png`

他坚持："我上传了 apk.png 啊！"

全场安静了两秒。

---

#### 七、真凶：hash 文件名和 OSS 上的 Key 不是一回事

Vite / Nuxt 会给静态资源加 content hash：

```text
源码里引用：apk.png
构建后文件名：apk.a1b2c3d4.png
页面运行时请求：/_nuxt/apk.a1b2c3d4.png
```

而 OSS 上实际存在的，往往是同事**手工补**的：

`apk.png`（无 hash，路径还可能不在 `_nuxt/`）

浏览器按 HTML/JS 里的 hash 名去要文件 → OSS 说没有 → 图片 404 → **按钮看起来就像"消失"了**（其实是图挂了，整块 UI 塌了或样式把空图藏了）。

💡 **本质一句话：构建产物引用的 Key，和部署桶里的 Key 不一致。** 不是玄学，是静态发布经典题。

---

#### 八、Jenkins 全绿，为啥还能翻车？

这是很多团队的真实误区：

**CI Success ≠ Runtime Resource Integrity**

Jenkins 绿灯通常只说明：

- 构建脚本执行成功
- 流水线没报错退出

**不保证**：

- OSS 已整包同步
- CDN 已刷新
- 没人往桶里手工塞错文件名的"救急包"
- 多节点、多路径资源一致

所以"构建成功 + 测试环境缺按钮"完全可以同时成立。

---

#### 九、这次 Agent 到底帮了啥忙？

别把它想成"自动修 Bug 神器"。这次有用的三件事：

1. **快速收窄范围**——Vue 逻辑、条件渲染、本地构建，很快排除；少刷无效 PR 和无效会议。
2. **跨层串线索**——页面路由 → chunk 引用 → hash 文件名 → OSS Key → 404，一条链拉通；单靠"我只看前端"很难这么快对齐。
3. **用证据代替吵架**——curl 结果、chunk 对比、NoSuchKey 路径，甩群里比"我觉得你没上传"管用一万倍。

它更像一个**不会累、还肯帮你跑命令的排障搭子**——最后拍板的还是人。

---

#### 十、四条血泪建议（建议贴发布 checklist）

1. **别只信 CI 绿灯**——上线后自动探活：关键 HTML、关键 JS、关键静态资源 URL 返回 200，才算真成功。
2. **静态资源整包同步**——Nuxt generate 后，同步整个 `.output/public/`，别只传 JS/CSS，更别漏 `_nuxt/` 里带 hash 的图。
3. **别往 OSS 根目录手工补"同名无 hash"文件**——现代前端认 hash，你补 `apk.png` 救不了 `apk.a1b2c3d4.png` 的 404。
4. **把资源一致性校验写进发布**——HTML 引用的每个 `_nuxt/*` 资源，部署后脚本扫一遍可访问性；很多团队缺的就是这一环。

---

#### 写在最后

以前总觉得这种"环境灵异 Bug"靠老司机嗅觉。

这次更大的感受是：**所谓玄学，多半只是缺一条完整证据链。**

Agent 擅长的，就是把散落各处的线索——仓库路径、构建目录、curl 输出、OSS 报错——**收敛成一个能验证、能复现、能写进复盘的故事**。

修可能还是人修；**少绕路、少背错锅、少开无效会**，就已经很值了。

我端起杯子——冰美式早凉了，但测试环境的 APK 按钮，总算在 OSS 里找到了它该有的 hash 文件名。

## 参考文章：相关链接

- [Nuxt · Assets / public directory](https://nuxt.com/docs/getting-started/assets)（官方文档）— 静态资源目录语义，避免把 OSS 缺失误判为 Nuxt Bug。
- [Aliyun OSS · Object storage overview](https://www.alibabacloud.com/help/en/oss/)（权威引文）— 对象存储权限与缓存，解释 APK 按钮「失踪」。
- [npm 依赖安装失败的精准排障之旅](https://yijinlee.com/articles/article-16)（站内深度）— 链路排障方法论的另一现场。
- [现网实录：“直接隐藏就行？”](https://yijinlee.com/articles/article-30)（站内深度）— 「看不见」类问题的产品侧对照。

