AI辅助Vue2到Vue3迁移:四天实战与工具链分享
说真的看到这个标题你可能觉得我在炫技。但实际情况是接到这个 Vue2 到 Vue3 迁移任务的时候我内心比谁都慌。业务方给的时间窗口只有四天代码库存量却是一点都不小——200 多个单文件组件、几十个路由页面、三四个状态管理模块还夹着若干“祖传插件”和内部二次封装的组件库。如果按传统流程来先手动梳理依赖再逐个文件改 API最后统一回归四天绝对不现实。所以这次我换了个思路大规模引入 AI 辅助。Claude 负责大批量代码扫描、差异改写和逻辑解释MiniMax 负责生成审阅摘要、补充命名方案和协助差异比对我只需要做架构决策、人肉审核和最终回归验证。这四天里实际验证下来这套组合是能跑通的而且效果超出预期。这篇文章不打算写什么“AI 取代程序员”的空话我只想把这套 AI 辅助迁移的工具链、提示词模板、批量改写流程和踩过的坑原原本本分享出来给正在做同类迁移的团队一个可参考的脚本。1. 项目整体情况与迁移思路1.1 项目体量与迁移难点先说说这个项目到底有多大。仓库本身是 Vue 2.6 搭建的 SPA 应用路由用的是 vue-router 3.x 的 history 模式状态管理是 Vuex 3UI 框架是基于 Element UI 做的内部二次封装自己维护了几套业务组件。整体规模大概是200 多个 .vue 文件、40 多个页面级路由、10 个左右全局混入和自定义指令另有三个模块的 Vuex store。这种项目的最大难点不是单个文件不好改而是“改动点分布在每一层”。组件里既要改模板语法又要改 script 里的 Options API还要处理全局 API 的变更路由入口要改store 的创建方式要改构建工具更得整个换。任何一个环节漏掉页面要么白屏要么编译失败要么运行时直接报 undefined。更麻烦的是项目里还存在不少历史遗留代码比如已经废弃的过滤器语法、混用了$on的跨组件通信、按需加载的异步组件写法……这些光靠搜索替换解决不了必须让人读懂逻辑后手动判断。我一开始也想过用传统方式“稳妥推进”但估算下来光是把所有组件扫一遍并理解业务逻辑就至少得花一个人将近两周。预算和排期都不允许。所以从立项开始我就决定用 AI 来做“编码助理”把人力从重复改写里释放出来集中投在审核和业务验证上。1.2 四天时间为什么可行四天听起来很紧张但如果把工作拆细其实是能排开的。我做的第一件事就是把整个迁移拆成四个阶段盘点、试点、批量迁移、回归验证。第一阶段用大半天主要做依赖整理、全仓扫描和风险点登记第二阶段用半天挑一个代表性页面跑通全链路验证 Claude 的改写质量和构建配置是否成立第三阶段是工作量重心花两天时间用 AI 批量改写组件、路由和 store并同步做编译修复最后一天专门留给回归验证、修显性问题和小范围兼容处理。这个排期的逻辑在于前两步相当于“探路”如果试点跑不通后面批量执行再快也是白搭。我最担心的问题从来不是 AI 改写速度而是“AI 生成的代码能不能进编译、跑通页面”。所以试点阶段我一定会自己锁定一个包含路由、store、组件嵌套、异步请求的页面完整验证一遍工具链。1.3 为什么首选 Claude Minimax 这套组合其实市面上能用的大模型不少我也不是没试过其他方案。最终选定 Claude 和 MiniMax主要出于三个互补性考虑。Claude 的核心优势是长上下文和代码理解能力。200 多个文件的迁移项目文件之间互相依赖我需要模型能一次性读入整个目录结构、理解 A 组件引用了 B store 的某个 mutation再给出改写意见。常规模型面对几千行代码时容易语义飘移但 Claude 在超长字段上表现很稳写出的大段.vue文件逻辑结构完整可读性好。尤其是v-model这类行为有细节差异的语法它给出的解释和改写都足够精准。MiniMax 在这个流程里的角色更多是“审阅助理”。我已经用它的 API 写了一小段批处理脚本扫描迁移后的临时文件让它生成问题摘要和差异说明。生成这类中文解释摘要时它的描述质量很高能帮我们快速定位“哪些文件被 AI 写坏了”“哪些文件我根本没改到”。另外如果你手头有本地部署的 MiniMax H 系列权重包也可以把同样的脚本打到本地推理服务上跑大批量扫描时几乎零成本很适合当“质检员”角色。总体而言大模型不一定要选“最贵的”选适合流水线分工的效果往往更好。2. AI 辅助迁移的工具链搭建2.1 Claude Code 的安装与权限设置我在迁移中用的主力交互工具是 Claude Code。它本质是一个运行在终端里的 AI 编码代理可以读取项目文件、执行命令、生成 diff很适合这种“站在项目根目录写作”的场景。安装方式很简单只要本地有 Node.js 环境执行一条命令即可npm install -g anthropic-ai/claude-code安装完成后在项目根目录执行claude就能进入交互模式。第一次启动它会检查认证信息配置好你的 API 或订阅凭据就行。这里有个容易被忽略的细节Claude Code 需要知道你允许它做哪些事。启动后它会询问是否允许读写文件和执行命令我的做法是在项目目录内逐项授权不图省事开全局权限。为什么我特别强调权限粒度因为在迁移场景里AI 会读很多老文件、生成一大堆新文件万一误删或覆盖不该动的东西损失非常大。逐项目授权、保留审批环节虽然看起来麻烦实际上只增加了十几分钟的确认成本却可以避开灾难性的返工。交互过程中我基本只用两类指令常规对话提需求以及/compact压缩上下文。迁移项目跑久了对话历史会非常长如果不加控制模型容易丢失早期需求细节。大概每处理 20 个文件之后我就会主动压缩一次上下文把已经完成的工作状态重新概述一遍再继续下一批任务。2.2 MiniMax 的接入与批量审阅脚本Claude 负责“写”MiniMax 负责“审”和“总结”这个分工合理之后我写了一个很薄的 Python 脚本用来批量调用 MiniMax 接口对迁移结果做第一轮质检。脚本并不复杂核心是用 requests 发起对话请求。因为现在不少模型都提供 OpenAI 兼容的接口格式所以我干脆按照这种格式去组织请求只要把模型的 base_url 和密钥换成对应服务的配置就行。如果你本地跑着 MiniMax 的模型权重也可以把 base_url 指向本地推理服务跑批量扫描时成本基本可以忽略。import requests API_KEY 你的密钥 BASE_URL https://api.minimax.chat/v1/text/chatcompletion_v2 MODEL_NAME MiniMax-Text-01 def review_file(file_path, content): resp requests.post( BASE_URL, headers{Authorization: fBearer {API_KEY}}, json{ model: MODEL_NAME, messages: [ {role: system, content: 你是资深 Vue 迁移审阅助手。你只做一件事分析给定的 Vue 文件内容列出所有疑似迁移遗漏、语法错误或与 Vue3 不兼容的地方按严重程度排序输出简洁的中文问题清单不要修改代码。}, {role: user, content: f文件路径{file_path}\n\n文件内容\n{content}} ] } ) return resp.json()[choices][0][message][content]实际使用中我会先把一个文件用 Claude 改写然后把改写后的结果喂给 MiniMax 做审阅。MiniMax 经常能挑出两类问题一类是 Claude 在改写时把变量名写错或漏引入另一类是注释里还残留 Vue2 术语、文档链接等旧信息。这比我人工逐字看效率高太多。需要提醒的是这个脚本里的模型名和接口地址记得以官方最新文档为准不同版本的服务商可能会有差异。但整体思路是可以复用的AI 辅助迁移不是只靠一个模型而是让两个模型互相交叉检查把出错概率降下来。2.3 提示词模板与任务拆解工具链搭好后真正决定迁移质量的是提示词。我在这四天里反复打磨出了几套固定模板这里挑三套最重要的分享。第一套是“全仓扫描模板”用在项目开始时。目的是让 Claude 先浏览整个目录结构输出一个迁移风险清单而不是一上来就动手改代码。我的写法是你是资深 Vue 工程师。请扫描项目 src 目录列出所有涉及 Vue2 特有 API 的文件和代码片段。重点查找 - Options API 中的 filters、$set、$delete、$on、$off - this.$scopedSlots 的使用 - 基于 Element UI 的组件二次封装 - vue-router 3 和 Vuex 3 的引入方式 - 模板中的 .sync 修饰符和具名插槽默认写法 输出格式文件路径 风险描述 建议改法。第二套是“单文件改写模板”用于批量迁移组件。它是全流程里最核心的提示词我要求模型必须保持组件的外部接口不变将以下 Vue2 Options API 组件改写为 Vue3 Composition API 语法。 要求 1. props、emit、v-model 等对外接口保持完全不变。 2. 使用 script setup 语法。 3. 保留原有业务逻辑和注释不进行额外重构。 4. 如果原组件使用了 computed 和 methods 且互相依赖拆解后保持调用关系不变。 5. 输出文件末尾列出所有“不确定项”例如 this.$nextTick 的时序差异、watch 深层监听行为、旧组件库的特殊用法。第三套是“构建错误修复模板”。迁移过程中经常出现编译报错这些错误比较机械适合让 AI 直接处理。我会把完整报错日志粘贴给它并附加相关文件的上下文。Claude 处理这类问题的准确率很高比我反复看终端日志找问题快得多。3. 核心迁移的实操环节3.1 依赖与构建工具的一次性升级任何 Vue2 到 Vue3 的迁移第一步都必须先把依赖和构建工具理顺。这个环节不建议用 Claude 直接改 package.json因为语义化版本号很容易搞混。我选择自己先拉一把全量清单把关键依赖的版本对应关系列成表逐项确认后交给 AI 辅助修改。下表是我这次迁移中用到的核心依赖对照一项Vue2 版本Vue3 版本迁移注意点vue2.6.x3.4.x直接升级后先跑通空项目再叠加改动vue-router3.x4.x创建方式从 new Router 变成 createRoutervuex3.x4.x 或弃用更推荐直接改用 Pinia少一层心智负担vue-cli5.x不用建议整体切换到 Vite后续收益远大于迁移成本element-ui2.xelement-plus组件 API 有变化二次封装层需要做适配vue-template-compiler2.x移除该包只服务 Vue2在 Vue3 下没有对应角色babel-plugin-component2.x移除或换 unplugin按需加载方案需要重写为 unplugin-vue-components我特别想多说一句切换到 Vite 的事。很多人保守起见会先保留 Vue CLI 的构建底座只升级 Vue 版本想着以后再说。但 Vue CLI 对 Vue3 的支持明显处于守成状态长期依赖一个维护不活跃的构建工具迁移后还是会不断遇到踩坑问题。Vite 本身的冷启动速度足以让开发体验上一个台阶而且它更早接入了 esbuild依赖预构建处理对大型依赖包很友好。所以这次我一步到位迁到了 Vite后面跑起来确实是真香。升级依赖时还有一个容易踩的坑如果你原本用vue-cli-service serve那么.env文件里的变量前缀、路径publicPath等配置都要跟着改。Claude 可以帮你把配置文件从vue.config.js翻译成vite.config.js但需要注意它对process.env的用法理解有时不完整关键在于那些自定义环境变量的取值方式。// vite.config.js 中环境变量示例 export default defineConfig({ base: process.env.VITE_PUBLIC_PATH || /, server: { port: 3000, proxy: { /api: { target: process.env.VITE_API_TARGET, changeOrigin: true } } } })3.2 Options API 到 Composition API 的批量改写这是整个迁移中最耗时、也最有技术含量的部分。传统的做法是一把梭地打开每个文件手动改这次我改成“Claude 批改 MiniMax 审阅 我抽查”的流水线。先照体现一下最常见的改写模式。假设有一个 Vue2 计数组件// Vue2 Options API export default { data() { return { count: 0, list: [] } }, computed: { doubleCount() { return this.count * 2 } }, watch: { count(newVal, oldVal) { console.log(newVal, oldVal) } }, mounted() { this.fetchList() }, methods: { fetchList() { // 请求数据 } } }Claude 改写后的 Vue3script setup版本大致是这样script setup import { ref, computed, watch, onMounted } from vue const count ref(0) const list ref([]) const doubleCount computed(() count.value * 2) watch(count, (newVal, oldVal) { console.log(newVal, oldVal) }) function fetchList() { // 请求数据 } onMounted(() { fetchList() }) /script按照这套提示词去批量跑Claude 的改写成功率大概在八成左右剩余两成通常集中在复杂组件上比如逻辑里大量依赖this.$refs、依赖跨组件事件通信或者存在异步竞态处理。处理这类复杂组件我会先让 Claude 只改一个文件然后亲自通读一遍不急着批量推进。批处理建议按业务模块切分不要对整个目录一次丢进去。我试过一次给 50 个文件让 Claude 全量改写结果跑到一半模型对某些公共模块的引用就开始混乱生成的script setup里变量名频繁写错。后来改成一次 10 个文件在每个批次之间让 MiniMax 做个快速校验准确率立刻上去了。这里还有个要命的小细节原组件如果有methods里互相调用的关系比如methodA调用methodB改写后必须保持同名函数存在只是作用域变得更封闭。如果 AI 把某些方法合并或删除会很难被静态检查发现只能靠跑页面测出来。这也是我坚持人工抽查的原因。3.3 路由与状态管理的迁移路由这块是重灾区因为 vue-router 3 和 4 的创建方式完全是两套写法。Vue2 是典型的“类实例化”方式需要在入口文件里用插件注册Vue3 则是“函数式创建”直接用createRouter返回实例。// Vue2 写法 import Vue from vue import Router from vue-router Vue.use(Router) export default new Router({ mode: history, routes })// Vue3 写法 import { createRouter, createWebHistory } from vue-router export default createRouter({ history: createWebHistory(), routes })路由守卫的写法也变了。Vue2 时代常用beforeEach挂在一个vue-router对象上或者通过this.$router.beforeEach去注册。Vue3 里所有导航守卫的注册方式都统一成从vue-router导出的方法这对我这种“守卫里写业务逻辑”的项目来说迁移成本不算低。建议把守卫逻辑单独抽成一个模块否则所有入口文件都要改一遍。状态管理我选的是直接把 Vuex 换成 Pinia。理由很简单Vuex 4 虽然支持 Vue3但它的 API 范式还是 Vuex 3 那套 mutation/action 分离复杂度一点都不低。而 Pinia 天然拥抱 Composition APIstore 定义可以写成接近普通业务函数的风格心智负担小很多。迁移示例// Vuex 3 写法 export default new Vuex.Store({ state: { userInfo: {} }, mutations: { SET_USER(state, payload) { state.userInfo payload } }, actions: { async fetchUser({ commit }) { const res await request(/user/info) commit(SET_USER, res.data) } } })// Pinia 写法 import { defineStore } from pinia export const useUserStore defineStore(user, { state: () ({ userInfo: {} }), actions: { async fetchUser() { const res await request(/user/info) this.userInfo res.data } } })表面上看只是 API 换了深层的变化是“mutation 不存在了”。所有对commit(SET_USER, ...)的调用都要改这对全仓影响面很大。我是靠 Claude 先做一遍全局搜索替换再让 MiniMax 把剩余的commit调用清单列出来逐个确认是改掉还是遗漏。整个过程不到一天就全部处理完。3.4 模板语法和全局 API 的差异处理如果说 script 部分的迁移是“重振”那模板部分更像“扫地”全是零零碎碎的小变动但漏一个就可能白屏。我在迁移中专门建了一个表把 Vue2 模板里常见写法与 Vue3 的对应关系列出来作为 Claude 和 MiniMax 的审阅依据。Vue2 写法Vue3 中如何处理迁移注意点{{ price | formatPrice }}过滤器已删除改为函数调用{{ formatPrice(price) }}需要在script setup中引入或定义函数组件上v-model默认绑定 valuev-model默认绑定 modelValue事件名改为 update:modelValue自定义组件必须显式声明 modelValue.sync修饰符改为v-model:propName写法子组件中 emit 事件名改为 update:propNamethis.$delete(obj, key)不再需要直接delete obj.key配合 reactiveVue3 响应机制自动监听属性删除this.$set(obj, key, val)不再需要直接赋值Vue3 的 Proxy 对象天然响应this.$children已移除使用$refs或 provide/inject找到实际使用场景逐个替换this.$scopedSlots合并到this.$slots通过函数调用插槽二次封装组件需要特别注意这里最坑的是过滤器。项目里大约十来个地方用了| formatPrice这种写法Vue3 直接不给过一编译就报错。好在它的改法非常机械Claude 处理起来毫不费力。真正费精力的是那些“隐式依赖”比如有人在computed里用了this.$store.state.user.name改到 Composition API 后必须显式在 setup 里import { useStore }这种问题不运行到页面根本无法发现只能靠冒烟测试兜住。4. 回归验证与异常排查4.1 构建产物与页面冒烟迁移完成不是终点验证才是。我的回归验证分成两层第一层是构建层面第二层是页面层面。构建验证最简单的方式是跑一次vite build看它能不能无报错产出生产包。但要多留个心眼Vite 的构建成功不代表代码逻辑正确它只证明语法和模块引用没大问题。我还会刻意对比迁移前后的构建产物体积和首屏数据如果出现异常增大通常说明某个公共模块被重复打包或者动态导入失效了。页面冒烟测试我采用的是“路由清单法”。把 40 多个路由页面全部列出来按业务优先级排序每天下班前由项目组成员跑一遍关键页面遇到白屏、报错、交互不可用直接把截图和 console 报错发给我我统一喂给 Claude 修复。这套流程保证了每天的推进都有反馈闭环而不是闷头把代码全改完才发现一切是坏的。4.2 运行时高频报错与修复四天里我收到了大量运行时错误高频错误集中在下面这几类。报错信息原因分析修复办法TypeError: this.$scopedSlots is not a function模板中仍使用旧插槽 API改写为this.$slots.header()形式或改模板插槽语法Cannot read properties of undefined读取 store 的属性setup 中没有正确引入 store 实例在script setup中用useUserStore()替换this.$store[Vue warn]: Property xxx was accessed during render but is not defined模板引用了未在 setup 中返回的变量检查是否漏写返回或引入的 ref 是否拼写不一致Uncaught TypeError: Cannot read properties of undefined (reading push)原data里数组未声明或赋错类型检查响应式数据初始化数组必须用ref([])或reactive({ list: [] })Element Plus 相关报错xxx is not a function二次封装的 Element UI 组件迁移后方法名变了逐组件对照 Element Plus 文档修改封装层这些错误里最烦人的是“变量名不一致”的问题。Claude 有时候会把keyword写成keyWord编译阶段根本发现不了只有跑到某个搜索页面输入内容时才会炸。解决这类问题只有一个笨办法让 MiniMax 对每个文件做一次变量名一致性检查看返回的结果里有没有“疑似赋值未使用”或“可能有变量名差异”的提示。4.3 没法自动化的边界场景虽然 AI 辅助大大提升了效率但也必须承认有些场景没法全自动。我这次迁移中遇到三类边界情况。第一类是旧依赖不兼容。项目里有内部自己改过的某个图表库只支持 Vue2帮不上 Vue3。这类硬骨头只能改造一层适配器或者换替代方案。AI 可以辅助分析但决定换还是不换必须人来拍板。第二类是组件库升级后的行为差异。Element UI 到 Element Plus表格组件、表单校验、弹窗组件的 API 都发生了细微变化这种变化不会立刻报错但会在具体业务场景里表现为样式错乱或事件回调不触发。我最后是用了一批“兼容混入”临时兜底再逐步修正的至少保证迁移上线期间业务不中断。第三类是模板里的“野路子”写法。有人用三目运算嵌套写复杂的 DOM 逻辑有人在生命周期钩子里直接操作 DOM。这些代码职责混乱AI 改写容易出错我的处理原则是业务逻辑不动只做“技术债记账”标记给后续重构排期。不能顺手改业务否则迁移冲突会无限扩大四天根本打不住。5. 常见问题与避坑实录5.1 问题速查表这次迁移中我和团队最终沉淀了一张“问题速查表”专门用于迁移过程中的自我检查。分享出来能省掉很多多研究的时间。问题快速定位方法标准解法页面白屏且无报错F12 看 network 和 console检查入口文件是否正确 mount路由 history 是否配置正确构建报Cannot resolve fs一类 Node 模块查看引用来源通常是某工具库需要 polyfill在 vite.config.js 里配置 define 和 resolve 别名或替换该库代码迁移后提示define is not defined大概率是把 vite 的 define 配置写错了检查环境变量替换写法 别名是否正确页面渲染正常但样式全乱检查组件库样式引入方式Element Plus 改用按需导入 unplugin-vue-components动态路由不生效查看路由守卫里是否还在用next()函数Vue Router 4 中守卫必须显式返回 true 或不调用 next异步组件加载失败控制台出现Failed to fetch dynamically imported module检查 import 路径大小写和实际文件名是否完全一致5.2 避坑心得写到最后分享几个这四天人肉踩坑换来的心得。第一个心得一定要给 AI 设定“不要做什么”。我在提示词里反复强调“保持 props 和 emit 不变”“不做额外重构”这两个约束让改写乱来的概率大幅下降。没有约束的 AI 改写经常会顺手把函数顺序调换、重命名变量虽然它自己觉得很好但 diff 会巨大无比人工 review 成本高到难以承受。第二个心得检查 AI 生成的代码重点看 diff而不是重新看一遍最终文件。如果是全量看文件人很容易被 AI 写得很规范的代码“催眠”看不出问题但如果是看 diff所有改动集中暴露一眼就能发现哪些地方动得不对劲。我所有组件抽查都只让 Claude 输出 diff 格式这一条建议预计可以帮你省下一半审核时间。第三个心得扩展阅读相关如果项目有单测迁移时保持测试文件及时更新。我这里项目单测覆盖率不高所以只能靠冒烟但如果你手上有现成单测一定要先跑一遍再改迁移后的绿档回归会给你极大安全感。没有测试的项目就只能寄希望于严格的人工抽查和运行时验证。第四个心得别同时把迁移和版本升级、功能重构混在一起。做 Vue2 到 Vue3 的时候任何多余的任务都会让 bug 定位变得极其困难。我也想过顺手把几个组件改成 TS 或者优化渲染逻辑后来都强行忍住了。干净利落地只做技术栈迁移是控制风险最好的办法。最后说个比较个人的经验。这四天下来我对“AI 辅助编码”的理解更具体了它最大的价值不是把代码写对而是把“重复劳动”和“人类验证”彻底分开。机器批量流水线式改写我专注在架构边界和问题排查上事半功倍。以后再做类似的框架升级我更倾向于把这种“两个 AI 互相审阅”的模式沉淀成团队的常规流程毕竟前端框架的迭代不会止于 Vue3。