案例背景
我们的客户是一家零售连锁企业,内部运营后台是 2020 年由外包团队用 jQuery + Bootstrap 写的单文件架构:一个 HTML 塞了 40+ 个页面的模板字符串(<script type="text/html">),一个 app.js 有 1.2 万行包含全局 AJAX 拦截器、权限控制、路由(手写 hash 路由)、页面切换。页面级 JS 有 4 万行,一共 5.2 万行纯 JS + HTML 字符串,没有打包工具、没有模块系统、没有测试。
为什么必须迁移:
- 新功能开发效率极低——改一个页面要在 1.2 万行的 app.js 里找对应的注册点,新人上手要 2 周
- 浏览器兼容性维护成本越来越高,新版 Chrome 已经报了好几个废弃 API 警告
- 排过两次全人工迁移计划,第一次评估 3 周/2 人,第二次缩减到 8 个核心页面也要 10 人天,业务窗口永远排不出来
这次契机是客户要给后台加一个"订单批量处理"模块,业务方给了 4 天窗口(周五晚上冻结 → 周二上线),我们承诺在窗口内完成 20 个核心页面的 jQuery→Vue3 迁移。最终用 AI Agent 实际花了 3 天完成。
工具组合与分工
| 工具 | 分工 | 为什么这样分工 |
|---|---|---|
| Cursor 3 | 批量迁移主力 | 多文件上下文强,Composer 模式可以一次拿 10+ 个页面的 jQuery 代码,批量生成等价 Vue3 SFC |
| Claude Code | 逐文件逻辑确认 + 边界兜底 | 复杂逻辑推理比 Cursor 稳,处理 $.ajax 拦截器、全局事件绑定这类"看不见的依赖" |
| Sequential Thinking MCP | 页面迁移的多步验证链路 | 每个页面迁移要走"提取→翻译→对照→自测→回滚"五步,中间发现对不上要能回退到上一步 |
| GitHub MCP | 自动化分支/PR/CI | 每个页面一个分支一个 PR,CI 跑过后合并,避免一次改太多回滚不了 |
关键原则:Cursor 批量出初稿,Claude Code 做精修,人只做确认。把最费时间的"机械翻译"交给 AI,人只盯逻辑一致性和最终页面效果。
实施步骤
第 0 步:先做"迁移骨架",不直接开始改页面(半天)
很多迁移项目失败是因为上来就让 AI 一页页改,结果每一页都各自为政,最后拼起来全局 AJAX 拦截器丢了、权限控制丢了、路由跳转逻辑不一致。
先把骨架用人工+AI混合写好(这部分不能全丢给AI,因为它是迁移的地基):
- 用 Vite + Vue3 + Vue Router + Pinia + Element Plus 建项目骨架
- 人工写三个全局基础模块:
src/utils/http.js:封装 axios,严格等价复刻 jQuery 的$.ajaxSetup+$.ajaxPrefilter行为(token 注入、401 跳登录、统一错误提示)src/stores/auth.js:Pinia 复刻原项目的全局权限变量 +checkPerm(code)函数src/router/index.js:路由注册和原 hash 路由的映射表
- 用 Claude Code + Sequential Thinking 走一遍"骨架自检"分支:
- Thought 1:列原项目 app.js 中所有
$(document).ajaxSuccess/Error/Complete回调 - Thought 2:逐一对照 http.js 中 axios 拦截器是否等价
- Thought 3:有遗漏就修订 http.js,没有就收口
- Thought 4:列所有
window.xxx全局变量,确认 pinia/auth 中有对应项
- Thought 1:列原项目 app.js 中所有
这一步花了半天,但它是后面 3 天能批量迁移而不翻车的核心——骨架错了,迁移的页面越多越快,返工量越大。
第 1 步:页面迁移标准化 Prompt 模板(第 1 天上午)
先拿 1 个简单页面当"样板间",跑通完整迁移流程并沉淀 Prompt 模板。后面 19 个页面直接套模板,避免每个页面重新想怎么让AI做。
Cursor 3 Composer 模式的迁移 Prompt(直接复用):
请帮我完成下面这个 jQuery 页面 → Vue3 SFC 的等价迁移。
## 输入材料
1. 原页面模板字符串(<script type="text/html" id="page-xxx">):
<template>
{{ original_template_html }}
</template>
2. 原页面 JS 逻辑(注册在 app.setupPages['page-xxx'] = function() {...} 中):
{{ original_page_js }}
## 等价迁移规则(违反任一即视为不合格)
1. HTML 结构 → <template>,jQuery 选择器操作 → Vue3 响应式 data
2. $.ajax({url, type, data, success}) → http.get/post + await
3. `checkPerm("xxx")` 函数保持原样调用(骨架已提供全局 import)
4. 原页面的 `onclick="doXxx()"` 行内事件 → `<button @click="doXxx">`
5. 原页面的日期格式 `$.format.date(d, "yyyy-MM-dd")` → dayjs(d).format("YYYY-MM-DD")
6. 原页面的 Bootstrap class 保持同名,Element Plus 组件只替换有明确等价物的(btn → el-button 等)
7. **绝对不能**改变 API 接口 URL、请求参数结构、返回数据字段名——这是逻辑漂移的最大来源
8. **绝对不能**"顺手优化"——先等价迁移再谈优化,优化 PR 单独提
## 输出要求
1. 直接输出一个完整的 .vue SFC 文件,路径按路由映射:src/views/{{pageName}}/index.vue
2. 文件末尾加 HTML 注释列出:原模板中哪些 class/id 被保留、哪些被改了
3. 如果有不确定是否等价的地方,在注释里标「⚠️ 需要人工确认」
## 最后:在提交前做"双向对照自检"
- 从原页面出发:每个 jQuery 事件绑定是否都有对应的 @xxx 或 onMounted
- 从新页面出发:每个 ref/reactive 变量是否在原页面中能找到对应的数据来源
- 两边对不上就不要硬写,标「⚠️ 需要人工确认」并说明卡在哪
拿"商品列表页"当样板间,跑通一次 + 人工确认无误(1.5小时),后面的页面直接套模板。
第 2 步:批量迁移 + PR 流水线(第 1 天下午 - 第 2 天全天)
这是纯机械+AI的环节,核心策略是 每页一个分支一个 PR,不攒大提交。流程如下:
对 20 个页面逐个循环 {
1. GitHub MCP: 建分支 feature/migrate-{pageName}
2. Cursor 3: 用上面的模板生成 SFC 文件
3. Claude Code + Sequential Thinking:
- Thought 1: 读原文件 + 读新 SFC
- Thought 2: 列 10 个关键逻辑点(接口/参数/条件分支/按钮事件)对照
- Thought 3: 每个点打分✅/⚠️/❌,❌的修订 SFC,⚠️的加注释
- Thought 4: 收口,输出"本页迁移可信度(0-100%)+ 待确认项清单"
4. GitHub MCP: 提 PR,触发 CI(ESLint + TypeScript 检查)
5. CI 通过 → 合并;CI 不通过 → 让 Cursor 自动修 lint 错误再 push
}
数据:
- 简单页面(列表+搜索+分页):15-25 分钟/页,可信度 95%+
- 中等页面(含弹窗+表单校验):30-45 分钟/页,可信度 85%-95%
- 复杂页面(含导入导出+多Tab联动+自定义插件):60-90 分钟/页,可信度 70%-85%
20 个页面中:14 个简单/中等 → 直接合并;6 个复杂 → 留下 1-3 个 ⚠️ 项,进入人工兜底阶段。
第 3 步:人工兜底 + 全链路联调(第 3 天全天)
6 个复杂页面留下了 13 个 ⚠️ 待确认项。人力只需要处理这 13 项,而不是从零写 6 个页面。典型的 ⚠️ 项包括:
| 典型问题 | 根因 | 处理方式 |
|---|---|---|
$.ajax 里有 context: this 指向特殊对象 | axios 默认没有等价机制 | 用 http.post(..., { extraContext }) + 拦截器特殊处理,单页面单独适配 |
$('.datepicker').datepicker() 依赖全局 jQuery UI 插件 | Vue 生态用 Element Plus DatePicker 但 API 不 1:1 | 人工对齐配置参数映射(dateFormat→value-format 等) |
原页面用 window.renderData = function(){...} 在弹窗里跨作用域调用 | Pinia 单例替代全局变量但调用链断了 | 把 renderData 抽到 composables/useTableReflow.ts,两边统一 import |
最后半天全链路联调:登录 → 走一遍 20 个页面的「搜索 → 查看详情 → 编辑 → 保存」主流程。只发现 2 个真实 bug(都是 ⚠️ 项中判断出错的),15 分钟修完。
关键 Prompt 与 Agent 行为约束
约束一:"禁止顺手优化"必须用反例约束焊死
这是迁移类任务最大的坑——AI 看到代码"写得不好"就忍不住重构,结果等价迁移变成了行为漂移。反例约束必须明确:
反例约束(绝对禁止)
- ❌ 禁止把「简单的 if 判断」改成「优雅的策略模式」——行为等价优先
- ❌ 禁止把「三次独立 API 请求」改成「并发 Promise.all」——时序可能变,原项目可能有后端依赖顺序
- ❌ 禁止移除原代码的「看起来没用」的空判断 / try-catch / 默认值——可能是之前修 bug 加的
约束二:双向对照自检(Cursor + Claude Code 各跑一遍)
两个模型各做一次双向对照,结果交叉验证。两边都打 ✅ 的高概率没问题,任一边打 ❌ 的必须人工看:
双向对照:
- 原 → 新:原代码每个接口调用、每个条件分支、每个事件绑定,在新代码里都能找到等价实现吗?
- 新 → 原:新代码每个 computed / watch / emit,在原代码里有真实的行为对应吗?还是你自己"优化"出来的?
效果数据
| 指标 | 纯人工原计划 | AI Agent 辅助 | 提升倍数 |
|---|---|---|---|
| 总工期 | 21 人天(2人×3周) | 6.5 人天(2人×3.25天) | 3.2× |
| 迁移页面数 | 20 个 | 20 个(目标达成) | 持平 |
| 其中 AI 独立完成(合并无需改) | — | 14 个(70%) | — |
| 留下人工兜底的 ⚠️ 项 | — | 13 项(6 个页面) | — |
| 联调发现真实 bug | 预估 8-15 个 | 2 个 | 约 5× 降低 |
| 上线后 1 周线上事故 | 预估 2-3 次 | 0 次 | ✅ |
| 代码行数变化 | 从 5.2 万行 → 预估 2.2 万行 | 从 5.2 万行 → 1.9 万行 | 略优于人工 |
经验总结
1. 骨架 + 样板页 + 批量 = 迁移公式
不要让 AI 直接开改,先花半天把「全局骨架」和「1 个样板页的迁移模板」搞定,后面批量才会快。跳过这一步 = 每页都在重新造轮子 + 行为漂移风险翻倍。
2. Cursor 批量出活 + Claude Code 精修 = 黄金组合
单靠 Cursor 会在"逻辑深度确认"上翻车(比如漏掉 $.ajax 的 context 参数),单靠 Claude Code 太慢。批量迁移让 Cursor 做初稿(快),逻辑对照让 Claude Code 做精修(稳),两个模型交叉验证比单模型置信度高一大截。
3. 每页一个 PR,不要攒大提交
迁移最怕的是"一次改太多,出了问题不知道是哪页引入的"。每页一个分支、一个 PR,CI 过了就合,CI 挂了就修当前页的问题。最坏情况:某页迁移失败 → 只 revert 这一页的 PR,不影响其他 19 页的进度。
4. "禁止优化"是写在 Prompt 里的硬规则,不是口头提醒
AI 看到"丑代码"会有本能的优化冲动。你口头说"别优化"没用,Prompt 里必须写成明确的反例约束 + 具体禁止清单。等价迁移是"行为不变",不是"代码更优雅"——优雅可以等迁移完、稳定了再开新 PR 做。
5. AI 迁移不是零人力,是"把人力从80%机械活移到20%判断活"
很多团队以为 AI 迁移就是"按个按钮全完事",结果真让 AI 全自由发挥,线上一周出 10 个 bug。正确的预期是:AI 把 80% 的机械翻译工作干掉,人力把 20% 的逻辑确认、全局骨架、跨页面边界问题兜住——这才是真实能落地的 Agent 工作流。