用阿里百炼批量重构 Vue2 到 Vue3(上):工具链搭建与组合式路线

这篇是 Vue2→Vue3 迁移系列的实操篇(上),聚焦组合式路线的完整流程。 如果你还在纠结"要不要迁"或"选哪条路线",建议先看 方案推演篇

这是一份早期探索的流程记录。不是最佳实践,更像是一本实验笔记——记录了哪些路走通了、哪些坑是怎么填的、哪些问题到最后也没解决。

为什么选百炼

方案推演篇 里提过,组合式路线重度依赖大模型。我选了阿里百炼(通义千问-Max-Latest),原因很现实:

  1. 当时团队内可免费调用,不用担心 token 成本
  2. 代码转换的 token 量巨大,按市价算不是个小数字
  3. 支持流式输出,代码量大的时候也能输出完
  4. 转换效果可以接受

整体思路

因为这个转换本质上是文件维度的操作——每个文件独立处理,对整个项目直接做一刀切的全量转换,大概率会因为大量不可预期的文件关联错误而翻车。

所以建议的做法是:

人工把项目切分成多个较小的可验证场景,渐进验证。随着 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.jsonnode_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',
    }
  }
})

第二步:搭建批量处理工具

我写了一个批量处理脚本,核心逻辑是:

  1. 遍历指定目录下的文件
  2. 对符合规则的文件,结合指定的 Prompt 发送给大模型
  3. 只取返回的代码部分,写入目标文件
  4. 记录处理进度,支持断点续传

关键能力:

  • 按文件规则匹配不同的 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 引用。

第四步:执行顺序

  1. 先执行 mixin 转 hooks
  2. 将转换后的 hooks 代码作为知识库,嵌入 .vue 文件的 Prompt
  3. 执行 .vue 文件转换
  4. 并行执行 .js 文件转换
  5. 最后手动处理特殊场景

踩过的坑

文件太大会超时

最初用同步方式调用 API,大文件经常超时。改成流式输出后解决了超时问题。但还有一个限制:输出 token 上限约 6000(对应 ~800 行代码),超长文件会被截断。

AI 会把 store module 误认为完整 store

如果一个文件里只有 statemutations,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 比组合式简单多少
  • 两条路线的混合策略

延伸阅读:

Comments