这篇是 Vue2→Vue3 迁移系列的实操篇(上),聚焦组合式路线的完整流程。 如果你还在纠结"要不要迁"或"选哪条路线",建议先看 方案推演篇。
这是一份早期探索的流程记录。不是最佳实践,更像是一本实验笔记——记录了哪些路走通了、哪些坑是怎么填的、哪些问题到最后也没解决。
为什么选百炼
在 方案推演篇 里提过,组合式路线重度依赖大模型。我选了阿里百炼(通义千问-Max-Latest),原因很现实:
- 当时团队内可免费调用,不用担心 token 成本
- 代码转换的 token 量巨大,按市价算不是个小数字
- 支持流式输出,代码量大的时候也能输出完
- 转换效果可以接受
整体思路
因为这个转换本质上是文件维度的操作——每个文件独立处理,对整个项目直接做一刀切的全量转换,大概率会因为大量不可预期的文件关联错误而翻车。
所以建议的做法是:
人工把项目切分成多个较小的可验证场景,渐进验证。随着 Prompt 越来越准,逐步扩大单次执行范围。
组合式 vs 选项式:先说清楚区别
在开始之前,先理解两条路线的核心差异:
| 维度 | 组合式路线 | 选项式路线 |
|---|---|---|
| 人工参与程度 | 高 | 低 |
| 测试成本 | 高 | 低 |
| 时间 | 长 | 短 |
| 可维护性 | 看团队目标 | 看团队目标 |
如果团队希望统一组合式写法,或者后续有二次升级计划(比如升级 UI 框架),组合式有长期优势。如果只是"先跑起来",选项式足够了。
这篇先讲组合式。
第一步:环境准备
备份源码
# 把 src 备份到 src-vue2,后续 AI 读取 vue2 源码、输出到 src
cp -r src src-vue2
echo "src-vue2" >> .gitignore处理依赖
这一步没法自动化。你需要手动对照 package.json,把 Vue2 生态的依赖升级到 Vue3 版本:
vue: ^2.6 → ^3.5
vue-router: ^3.4 → ^4.5
vuex: ^2.3 → ^4.1
element-ui: ^2.15 → element-plus: ^2.9
eslint-plugin-vue: ^6 → ^8
同时需要:
- 删除
vue-template-compiler(Vue3 不需要) - 添加
@vitejs/plugin-vue(如果从 webpack 切到 vite) - 删除
package-lock.json和node_modules,重新安装
一些只支持 Vue2 的私有包,要么找替代,要么暂时移除。这部分最耗时,但没有捷径。
添加 Vite 配置
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
export default defineConfig({
plugins: [vue()],
resolve: {
extensions: ['.mjs', '.js', '.ts', '.jsx', '.tsx', '.json', '.vue'],
alias: {
'@': '/src',
'src': '/src',
}
}
})第二步:搭建批量处理工具
我写了一个批量处理脚本,核心逻辑是:
- 遍历指定目录下的文件
- 对符合规则的文件,结合指定的 Prompt 发送给大模型
- 只取返回的代码部分,写入目标文件
- 记录处理进度,支持断点续传
关键能力:
- 按文件规则匹配不同的 Prompt——mixin 文件用一套 prompt,.vue 文件用另一套
- 流式输出——解决大文件超时截断问题
- 进度记录——600+ 文件不可能一次跑完,需要知道跑到哪了
第三步:Prompt 工程(核心)
分三种文件类型,各有一套 Prompt。这是整个方案最重要的部分。
Prompt 1:mixin 转 hooks
mixin 必须最先处理,因为后续 .vue 文件的转换需要知道 mixin 变成了什么样的 hooks。
## 技能 1:语法和方法声明
- 确保代码符合 ES6 模块系统
## 技能 2:重构 mixin 为 hooks
- 将 Vue2 的 mixin 转换为 Vue3 组合式 API(hooks)
- 所有 hooks 通过单独导出,不使用 export default
- hooks 增加 mixinProps 入参,用于接收原 methods 中的方法
- 如果 mixinProps 中有对应方法,优先执行 mixinProps 的方法
## 技能 3:处理 this 相关的问题
- this.$store → vuex4 写法
- this.$emit → defineEmits 事件声明方式
- this.$router → useRouter()
- this.$route → useRoute()
- this.$refs.xxx → vueInstance.refs.xxx
- 其他 this.xxx → vueInstance.setupState.xxx
(vueInstance 通过 getCurrentInstance() 获取)
## 限制
- 即使不需要修改也返回原代码
- 只返回代码文本,不返回 markdown 格式
mixinProps 的设计是个关键点——它解决了一个常见问题:组件在引用 mixin 时可能覆盖了某些方法,转成 hooks 后这个覆盖关系不能丢。
Prompt 2:通用 .js 文件
## 技能 1:语法和方法声明
- 确保符合 ES6 模块系统
## 技能 2:Vue3 代码重构
- 将 Vue2 代码重构为非 TypeScript 的 Vue3 代码
- this.xx → vueInstance.proxy.xx
- Vue.prototype → vueInstance.proxy
## 技能 3:mixin 转换
- 将 mixin 引用改为 hooks 引用,结合知识库内容重写
- 不改变原本的导入地址
## 技能 4:JSX 语法转换
- return (<></>) 改为 h() 函数渲染
## 技能 5:路由处理
- 避免变量声明与 createRouter 冲突
## 技能 6:Vuex 处理
- store module 文件不需要 createStore 重构
## 技能 7:国际化处理
- VueI18n → createI18n
## 技能 8:动态引入
- require → import.meta.glob(Vite 方式)
## 限制
- 不改变导入地址
- 不对 module store 重构
- 保留代码注释
- 只返回代码文本
Prompt 3:.vue 文件(最复杂)
.vue 文件的 Prompt 在 .js 的基础上,额外需要:
- 使用
<script setup>风格 - template 中有 ref 属性的标签要转为 Vue3 的
ref()方式 - 最关键:把前面生成的 hooks 代码作为"知识库"附在 prompt 末尾
# 知识库
## mixin 的 searchMixin 转为 hooks 的 useTableData
// useTableData 的完整代码...
这样 AI 就知道了 mixin 和 hooks 的对应关系,能正确处理 .vue 文件中的 mixin 引用。
第四步:执行顺序
- 先执行 mixin 转 hooks
- 将转换后的 hooks 代码作为知识库,嵌入 .vue 文件的 Prompt
- 执行 .vue 文件转换
- 并行执行 .js 文件转换
- 最后手动处理特殊场景
踩过的坑
文件太大会超时
最初用同步方式调用 API,大文件经常超时。改成流式输出后解决了超时问题。但还有一个限制:输出 token 上限约 6000(对应 ~800 行代码),超长文件会被截断。
AI 会把 store module 误认为完整 store
如果一个文件里只有 state 和 mutations,AI 会自作主张用 createStore 重写它。但这个文件可能只是一个 module,不需要 createStore。
解决方案:在 Prompt 里明确写 "不对 module 的 store 文件进行 vuex 的相关重构"。
mixin 是个黑盒
Vue2 的 mixin 在引用它的组件中看不到它导出了什么。AI 缺少信息,就会生成离谱的代码。
解决方案:先把 mixin 转成 hooks,再把 hooks 的完整代码作为知识库喂给 AI。
动态 key 创建响应式变量
有些 mixin 通过外部传入的 key 动态创建 data 属性,AI 处理这种场景不稳定。
解决方案:在 Prompt 中指导使用 getCurrentInstance() 获取实例上下文,用 vueInstance.setupState 替代 this。
变量名冲突
.vue 文件中如果 data 里有个变量叫 value,同时某个方法里也有局部变量叫 value,转成 ref 后就会出现 value.value = value 的尴尬情况。
这个目前没有完美解决,需要人工 review。
需要手动处理的文件
有些文件 AI 处理的成本太高或者场景太特殊:
- 黑盒注入全局事件的文件:有些工具文件在
import时就会注入全局副作用,AI 会把它改成 hooks 触发式注入。改造方向是对的,但需要手动调整调用方式。 - 使用
Vue.use的非入口文件:Vue3 只允许在createApp实例上注册插件,散落在各处的Vue.use需要手动把 app 实例传进去。 - 私有包依赖 Vue2:没有 Vue3 版本的私有包,要么重写要么移除。
- 样式问题:不同项目差距太大,AI 基本帮不上忙。建议从组件库入手找依赖更新的差异点。
时间数据
从实际执行来看,一个约 600 个文件的项目:
- mixin 转换:约 30 分钟(文件少,prompt 简单)
- JS 文件转换:约 2-3 小时
- Vue 文件转换:约 4-5 小时(prompt 最复杂,知识库最大)
- 手动处理:取决于项目复杂度,一般 1-2 天
全流程串行大约 8 小时 AI 执行时间 + 1-2 天人工调试。可以开多个终端并行加速 AI 执行部分。
下篇预告
下篇会讲:
- 选项式路线——用 gogocode 做基础转换 + AI 补刀
- gogocode 的已知问题清单
- 选项式 prompt 比组合式简单多少
- 两条路线的混合策略
延伸阅读:
- 方案推演篇:Vue2→Vue3 路线选择
- gogocode — 基于 AST 的 Vue2 到 Vue3 转换工具