Claude Code 将 Vue2 老项目重构为 Vue3 + TS 详细指南

用 Claude Code 将 Vue2 老项目重构为 Vue3 + TS 详细指南

本文提供一套完整的渐进式重构思路,以及可直接复制使用的 Claude Code 提示词,帮助你将 Vue2 项目平稳迁移到 Vue3 + TypeScript + Element Plus + Pinia 技术栈。


一、重构整体策略

1.1 核心原则:渐进式重构,而非推倒重来

老项目重构最大的风险不是技术难度,而是业务中断。因此推荐采用渐进式分阶段重构策略:

阶段目标风险等级是否可并行开发
Phase 0基础设施准备(构建工具、依赖升级)
Phase 1Vue2 → Vue3(兼容模式)⭐⭐
Phase 2ElementUI → Element Plus⭐⭐⭐否(UI 层需统一)
Phase 3Vuex → Pinia⭐⭐是(可共存)
Phase 4JavaScript → TypeScript⭐⭐是(逐文件迁移)
Phase 5清理兼容层,全面拥抱 Vue3

1.2 为什么用 Claude Code 而不是手动重构

  • 速度快:批量替换 API、组件属性映射等机械性工作,AI 比人快 10 倍以上
  • 一致性高:同一类改动不会出现遗漏或风格不统一
  • 边改边学:AI 会解释每处改动的原因,团队成员可以边重构边学习 Vue3
  • 可回退:按文件/按模块提交,出问题可以精准回滚

二、Phase 0:基础设施准备

2.1 重构前的准备工作

在让 AI 动手之前,先做好这些准备:

  1. 创建独立分支git checkout -b refactor/vue3-migration
  2. 锁定当前版本:确保 package-lock.jsonyarn.lock 已提交
  3. 跑通测试:确保重构前所有测试通过,作为基准
  4. 梳理项目结构:让 AI 先了解项目全貌

2.2 构建工具升级建议

当前工具推荐升级为原因
Vue CLIVite开发体验提升巨大,Vue3 官方推荐
Webpack 4Webpack 5 + vue-loader@16渐进式升级,改动较小
Babel + ESLint@vitejs/plugin-vue + eslint-plugin-vue配合 Vite 使用

三、Phase 1:Vue2 → Vue3 核心迁移

3.1 迁移思路

Vue3 提供了 @vue/compat 兼容模式,可以让 Vue2 代码在 Vue3 环境下运行,然后逐步移除兼容模式。

推荐路径

  1. 先升级到 Vue 2.7(Vue2 的最后一个版本,内置 Composition API)
  2. 新组件用 <script setup> + Composition API 写
  3. 老组件逐步改造
  4. 全部改完后切换到 Vue3 正式模式

3.2 核心 API 变化速查表

Vue2Vue3说明
new Vue()createApp()应用实例创建方式
Vue.use()app.use()插件注册
Vue.component()app.component()全局组件注册
Vue.prototype.$xxxapp.config.globalProperties.$xxx全局属性
this.$emitdefineEmits()事件定义
props: { ... }defineProps<{ ... }>()Props 定义(TS 方式)
data() { return {} }ref() / reactive()响应式数据
computed: {}computed(() => {})计算属性
watch: {}watch() / watchEffect()侦听器
methods: {}普通函数方法直接定义
filters移除,用方法或计算属性替代过滤器已废弃
$listeners移除,合并到 $attrs监听器
.native 修饰符移除原生事件修饰符
v-model (prop: value, event: input)v-model (prop: modelValue, event: update:modelValue)v-model 变化
.syncv-model:propName替换 .sync
slot-scopev-slot插槽语法
keep-alive + router-view<router-view v-slot="{ Component }">路由缓存写法
transition 类名v-enter-from 替代 v-enter过渡类名
destroyed / beforeDestroyunmounted / beforeUnmount生命周期
Vue.set / Vue.delete直接赋值 / 删除Proxy 代理无需 set

3.3 Claude Code 提示词:Vue2 组件转 Vue3

提示词 1:单文件组件整体迁移(Options API → Composition API)
你是一个 Vue3 迁移专家。请将以下 Vue2 单文件组件(SFC)重构为 Vue3 的 <script setup> + Composition API 写法,要求:

1. 使用 <script setup lang="ts"> 语法
2. data 中的数据用 ref() 或 reactive() 转换(简单值用 ref,对象用 reactive)
3. computed 用 computed() 函数重写
4. watch 用 watch() 函数重写
5. methods 中的方法直接转为普通函数
6. 生命周期钩子对应替换:
   - created / beforeCreate → 直接写在 setup 顶层
   - mounted → onMounted
   - beforeDestroy → onBeforeUnmount
   - destroyed → onUnmounted
   - beforeUpdate → onBeforeUpdate
   - updated → onUpdated
7. props 用 defineProps<{}>() 泛型方式定义,并加上类型
8. emit 用 defineEmits<{}>() 定义
9. 过滤器(filters)改为普通工具函数
10. 保持原有业务逻辑完全不变,只改写法
11. 模板部分:
    - slot-scope 改为 v-slot
    - .sync 改为 v-model:xxx
    - .native 修饰符移除
    - v-for 的 key 位置保持正确
12. 样式部分保持不变
13. 在关键改动处添加注释说明为什么这么改

请直接输出完整的重构后文件内容,不要省略任何部分。

待重构的组件代码如下:

---
{粘贴你的 Vue2 组件代码}
---
提示词 2:批量迁移一个目录下的所有组件
你是一个 Vue3 迁移专家。我需要你帮我批量重构 src/components/ 目录下的所有 Vue2 组件为 Vue3 <script setup> + TypeScript 写法。

要求:
1. 逐个文件处理,每次处理完一个文件后等待我确认再继续下一个
2. 保持组件的功能完全一致,不做任何业务逻辑改动
3. 使用 <script setup lang="ts"> 语法
4. props 和 emit 都用 TypeScript 类型定义
5. 响应式数据合理选择 ref 或 reactive
6. 模板中的 Vue2 特有语法也要同步更新
7. 每个文件处理完后,用列表总结本次改动的要点(5 条以内)

请先列出 src/components/ 目录下的所有 .vue 文件,然后从第一个开始处理。
提示词 3:main.js / main.ts 入口文件迁移
请将以下 Vue2 的 main.js 入口文件重构为 Vue3 的 main.ts 写法,要求:

1. new Vue() 改为 createApp()
2. Vue.use() 改为 app.use()
3. Vue.component() 改为 app.component()
4. Vue.prototype 改为 app.config.globalProperties
5. 引入方式从 'vue' 改为正确的 Vue3 引入
6. 如果有 Vuex,先保持 Vuex 的写法(后续单独迁移)
7. 如果有 ElementUI,先保持(后续单独迁移)
8. 加上必要的 TypeScript 类型
9. 保持所有插件和配置的顺序不变

原始代码:

---
{粘贴你的 main.js 代码}
---

四、Phase 2:ElementUI → Element Plus

4.1 迁移思路

ElementUI 和 Element Plus 的 API 有不少差异,但整体组件命名和使用方式比较接近。建议:

  1. 先全局替换前缀el- 前缀不变,但组件名的大小写规范要统一
  2. 逐个组件检查:重点关注那些 API 变化较大的组件
  3. 样式变量替换:SCSS 变量名变化较大

4.2 主要变化速查表

ElementUI (Vue2)Element Plus (Vue3)说明
Vue.use(ElementUI)app.use(ElementPlus)全局注册
this.$messageElMessage消息提示(需手动导入)
this.$confirmElMessageBox.confirm确认框
this.$alertElMessageBox.alert警告框
this.$promptElMessageBox.prompt输入框
this.$notifyElNotification通知
this.$loadingElLoading加载
size="medium"size="default"尺寸变化
type="info"type="info"(多数不变)类型基本不变
el-tableprop不变表格列属性
el-dialogvisible.syncv-model="visible"对话框
el-drawervisible.syncv-model="visible"抽屉
el-selectvalue / labelvalue / label(基本不变)选择器
el-formlabel-position不变表单
el-paginationcurrent-page.syncv-model:current-page分页
el-tabsvaluev-model标签页
el-menudefault-activedefault-active(不变)菜单
el-treenode-key不变树形控件
el-uploadfile-listfile-list(基本不变)上传
主题色 --color-primary--el-color-primaryCSS 变量前缀变化
$--color-primary@use 'element-plus/theme-chalk/src/index'SCSS 变量引入方式变化
el-icon-xxx<el-icon><xxx /></el-icon><component :is="xxx" />图标使用方式大变

4.3 高风险组件(需要重点检查)

以下组件的 API 变化较大,建议人工 review:

  1. el-table:部分事件参数变化、排序/筛选 API 调整
  2. el-form:校验规则的类型定义变化大
  3. el-upload:文件列表结构、上传钩子参数变化
  4. el-tree:节点数据结构、节点操作方法变化
  5. el-date-picker:值格式、快捷选项 API 变化
  6. el-select:远程搜索、虚拟滚动 API 变化

4.4 Claude Code 提示词:ElementUI 转 Element Plus

提示词 1:单组件 ElementUI → Element Plus 迁移
你是一个 Element Plus 迁移专家。请将以下 Vue 组件中的 ElementUI 用法全部替换为 Element Plus 的写法,要求:

1. 组件名保持 el- 前缀不变(Element Plus 也用 el- 前缀)
2. 重点检查并替换以下内容:
   - this.$message → ElMessage(需 import { ElMessage } from 'element-plus')
   - this.$confirm → ElMessageBox.confirm
   - this.$alert → ElMessageBox.alert
   - this.$notify → ElNotification
   - this.$loading → ElLoading.service
   - visible.sync → v-model(dialog、drawer 等)
   - value.sync → v-model:value(或对应 prop 名)
   - el-icon-xxx 类名方式 → <el-icon><xxx /></el-icon> 组件方式
   - size="medium" → size="default"
3. 检查所有 Element Plus 组件的 props 是否有重命名
4. 检查事件名是否有变化
5. 保持组件的功能和 UI 表现完全一致
6. 如果某个属性在 Element Plus 中已废弃,用推荐的替代方案替换
7. 样式相关的类名如果有变化也同步更新
8. 在改动处添加注释说明

请直接输出完整的重构后文件内容。

待重构的组件代码如下:

---
{粘贴你的组件代码}
---
提示词 2:图标批量替换
请帮我将以下代码中所有 ElementUI 的图标使用方式(el-icon-xxx 类名)替换为 Element Plus 的组件式图标写法。

规则:
1. <i class="el-icon-xxx"></i> → <el-icon><xxx /></el-icon>
2. 图标名的转换:el-icon-xxx → 大驼峰命名,例如:
   - el-icon-edit → Edit
   - el-icon-delete → Delete
   - el-icon-search → Search
   - el-icon-user → User
   - el-icon-setting → Setting
3. 需要在 script 中 import 对应的图标:import { Edit, Delete } from '@element-plus/icons-vue'
4. 如果是在按钮的 icon 属性中使用(<el-button icon="el-icon-edit">),改为 :icon="Edit"
5. 保持所有其他代码不变

请输出完整的修改后代码,并列出所有替换的图标清单。

代码如下:

---
{粘贴你的代码}
---
提示词 3:SCSS 主题变量迁移
请将以下 SCSS 文件中的 ElementUI 主题变量替换为 Element Plus 的主题变量。

替换规则:
1. $--color-primary → $--el-color-primary(加 el- 前缀)
2. $--color-success → $--el-color-success
3. $--color-warning → $--el-color-warning
4. $--color-danger → $--el-color-danger
5. $--color-info → $--el-color-info
6. 所有 $-- 开头的 ElementUI 变量都加上 el- 前缀,变为 $--el-
7. 检查变量名是否有其他变化(部分变量在 Element Plus 中已重命名)
8. 如果使用了 @import "~element-ui/packages/theme-chalk/src/index"; 改为 Element Plus 的对应引入方式
9. 自定义覆盖变量的写法保持不变

请输出修改后的完整 SCSS 内容。

SCSS 代码如下:

---
{粘贴你的 SCSS 代码}
---

五、Phase 3:Vuex → Pinia

5.1 迁移思路

Pinia 是 Vue3 官方推荐的状态管理库,比 Vuex 更简洁、TypeScript 支持更好。

推荐路径

  1. 先安装 Pinia,与 Vuex 共存
  2. 逐个 module 迁移到 Pinia store
  3. 组件中逐步替换引用
  4. 全部迁移完后移除 Vuex

5.2 核心概念映射

VuexPinia说明
statestate状态,函数返回对象
mutations移除,直接修改 state不需要 mutations 了
actionsactions支持同步和异步
gettersgetters计算属性
modules多个独立的 store没有嵌套模块了
commit('xxx')直接赋值 store.xxx = yyy直接修改
dispatch('xxx')store.xxx()直接调用方法
mapStatestoreToRefs()解构保持响应式
mapGettersstoreToRefs()同上
mapActions直接解构函数函数可直接解构
命名空间 namespaced: true每个 store 自动独立天然命名空间

5.3 Claude Code 提示词:Vuex 转 Pinia

提示词 1:单个 Vuex module 转 Pinia store
你是一个 Pinia 迁移专家。请将以下 Vuex module 重构为 Pinia 的 defineStore 写法,要求:

1. 使用 defineStore 定义 store,id 用原 module 的名称
2. state 保持为函数返回对象,转为箭头函数
3. mutations 全部移除,改为直接在 actions 中修改 state
4. actions 中的方法:
   - 移除 context 参数,直接用 this 访问 state 和其他 actions
   - 所有 commit('xxx', payload) 改为直接修改 this.state 属性
   - 所有 dispatch('xxx', payload) 改为直接调用 this.xxx(payload)
5. getters 保持,用 state 参数访问状态
6. 使用 TypeScript 为 state 添加类型定义
7. 保持所有业务逻辑完全一致
8. 如果有嵌套的子模块,拆分为独立的 store
9. 在关键改动处添加注释

请输出完整的重构后 store 文件内容。

Vuex module 代码如下:

---
{粘贴你的 Vuex module 代码}
---
提示词 2:组件中 Vuex 用法替换为 Pinia
请将以下 Vue 组件中所有 Vuex 的用法替换为 Pinia 的用法,要求:

1. import { mapState, mapGetters, mapActions } from 'vuex' → import { useXxxStore } from '@/stores/xxx'
2. computed 中的 mapState / mapGetters → const store = useXxxStore(); const { xxx, yyy } = storeToRefs(store)
3. methods 中的 mapActions → const { action1, action2 } = useXxxStore()
4. this.$store.state.xxx → store.xxx
5. this.$store.commit('xxx', val) → store.xxx = val 或对应的 action
6. this.$store.dispatch('xxx', val) → store.xxx(val)
7. this.$store.getters.xxx → store.xxx
8. 保持组件功能完全一致
9. 需要 import storeToRefs from 'pinia'

请输出完整的修改后组件代码。

组件代码如下:

---
{粘贴你的组件代码}
---
提示词 3:Vuex 插件/中间件迁移
请将以下 Vuex 插件代码迁移为 Pinia 插件写法,要求:

1. Vuex 插件的 (store) => {} 形式 → Pinia 插件的 ({ store }) => {} 形式
2. store.subscribe → Pinia 的 store.$subscribe
3. store.subscribeAction → Pinia 的 store.$onAction
4. 持久化插件等常用插件,如果有 Pinia 版本建议直接替换(如 pinia-plugin-persistedstate)
5. 保持插件的功能完全一致
6. 添加 TypeScript 类型

请输出迁移后的完整插件代码。

Vuex 插件代码如下:

---
{粘贴你的插件代码}
---

六、Phase 4:JavaScript → TypeScript

6.1 迁移思路

TS 迁移是最耗时的阶段,建议从下往上迁移:

  1. 工具函数 / utils → 最容易,纯函数
  2. 类型定义 / interfaces → 先定义好共享类型
  3. Store(Pinia) → 状态层类型很重要
  4. 组件 → 从简单组件开始,逐步到复杂组件
  5. 入口文件 / 配置文件 → 最后处理

6.2 关键类型定义

场景Vue2 JSVue3 TS
Propsprops: ['name', 'age']defineProps<{ name: string; age: number }>()
Emitsemits: ['update']defineEmits<{ (e: 'update', val: string): void }>()
Ref-const count = ref<number>(0)
Reactive-const state = reactive<User>({ name: '', age: 0 })
Computed-const double = computed<number>(() => count.value * 2)
组件实例thisconst instance = getCurrentInstance()
路由this.$routerconst router = useRouter()
路由参数this.$route.paramsconst route = useRoute(); route.params
全局属性this.$xxx类型声明扩展 ComponentCustomProperties

6.3 Claude Code 提示词:JS 转 TS

提示词 1:工具函数 JS → TS
请将以下 JavaScript 工具函数文件转换为 TypeScript,要求:

1. 为所有函数参数添加类型注解
2. 为所有函数返回值添加类型注解
3. 为内部变量推断或添加类型
4. 导出的函数和变量都要有明确类型
5. 如果有对象参数,定义对应的 interface 或 type
6. 如果有回调函数,定义函数类型
7. 保持所有逻辑完全不变
8. 尽量利用 TypeScript 的类型推断,不要过度标注
9. 文件扩展名改为 .ts

请输出完整的转换后代码。

JS 代码如下:

---
{粘贴你的 JS 代码}
---
提示词 2:Vue 组件 JS → TS(
请将以下 Vue 组件的 JavaScript 部分转换为 TypeScript,使用 <script setup lang="ts"> 语法,要求:

1. props 用 defineProps<{}>() 泛型方式定义,每个 prop 都要有明确类型
2. emit 用 defineEmits<{}>() 定义,每个事件都要有参数类型
3. ref() 变量如果初始值不能推断类型,加上泛型参数
4. reactive() 对象定义对应的 interface
5. computed 加上返回值类型(如果推断不出来)
6. 函数参数和返回值都加上类型
7. 事件处理函数的 event 参数加上正确类型(如 Event, MouseEvent, InputEvent 等)
8. 导入的第三方库如果有类型,确保类型正确
9. 保持所有业务逻辑完全不变
10. 模板部分不需要改动

请输出完整的转换后组件代码。

组件代码如下:

---
{粘贴你的组件代码}
---
提示词 3:生成类型定义文件
请根据以下 API 接口的返回数据结构,生成对应的 TypeScript 类型定义文件。

要求:
1. 为每个数据结构定义 interface
2. 嵌套对象也要定义对应的 interface
3. 数组类型明确标注元素类型
4. 可选属性用 ? 标记
5. 枚举值用 enum 或联合类型
6. 接口命名使用大驼峰,语义清晰
7. 加上适当的注释说明
8. 全部 export

API 返回数据示例:

---
{粘贴 API 返回的 JSON 数据或接口文档}
---

七、Phase 5:全面拥抱 Vue3(清理阶段)

7.1 清理清单

当所有模块都迁移完成后,进行最终清理:

  • 移除 @vue/compat 兼容模式
  • 移除 Vuex 依赖
  • 移除 ElementUI 依赖
  • 移除所有 // @ts-ignoreany 类型(尽量)
  • 统一 Composition API 写法,移除 Options API
  • 启用严格模式 strict: true
  • 所有组件都加上 name(可选,Vue3 不再必须)
  • 全局注册的组件改为按需引入
  • 跑通所有测试
  • 完整回归测试

7.2 Claude Code 提示词:代码审查与优化

你是一个 Vue3 + TypeScript 代码审查专家。请审查以下 Vue3 组件代码,检查是否存在以下问题并给出优化建议:

1. 是否还有 Vue2 的遗留写法(如 this.$xxx, filters, .sync 等)
2. TypeScript 类型是否完整,有没有 any 或 @ts-ignore
3. Composition API 的使用是否规范
4. 响应式数据的使用是否合理(ref vs reactive 的选择)
5. 有没有可以用 computed 优化的计算逻辑
6. 有没有内存泄漏风险(如未清理的定时器、事件监听)
7. 组件是否可以拆分为更小的组件
8. 命名是否规范
9. 有没有可以用 Vue3 新特性优化的地方(如 teleport, suspense 等)

请按以下格式输出:

## 问题清单
1. [问题类型] 问题描述 → 优化建议
2. ...

## 优化后代码
{完整的优化后代码}

待审查的组件代码:

---
{粘贴你的组件代码}
---

八、完整重构流程 Prompt(一键启动)

如果你想让 Claude Code 帮你规划整个重构流程,可以用这个提示词:

你是一位资深的 Vue3 架构师和重构专家。我有一个 Vue2 老项目,需要全面重构为 Vue3 + TypeScript + Element Plus + Pinia。

项目基本情况:
- 项目规模:{填写:小型/中型/大型,约 N 个组件}
- 当前技术栈:Vue 2.x + ElementUI + Vuex + JavaScript + {构建工具}
- 项目类型:{管理后台/移动端/官网/其他}
- 是否有测试:{是/否}
- 团队规模:{N 人}
- 期望完成时间:{N 周}

请你:
1. 先帮我制定一个详细的分阶段重构计划,每个阶段包含:
   - 阶段目标
   - 预计时间
   - 具体步骤
   - 风险点和应对方案
   - 验收标准

2. 给出每个阶段的最佳实践和避坑指南

3. 推荐一些有用的工具和库来辅助迁移

4. 制定一个代码审查 checklist,确保重构质量

请用结构化的方式输出,让我可以直接按照计划执行。

九、常见问题与避坑指南

Q1:重构过程中业务还在迭代怎么办?

A:采用「双轨并行」策略:

  • 主分支继续开发新功能(Vue2 版本)
  • 重构分支定期从主分支合并
  • 重构完成后一次性切换
  • 或者:新功能直接在重构分支用 Vue3 写,老功能逐步迁移

Q2:第三方组件库不支持 Vue3 怎么办?

A

  1. 找 Vue3 替代方案(大部分主流库都有 Vue3 版本)
  2. @vue/compat 兼容模式暂时撑着
  3. 自己封装适配层
  4. 实在不行就 fork 一份自己改

Q3:TypeScript 报错太多,改不动怎么办?

A

  1. 先开 strict: false,逐步加严格
  2. 先用 any 顶着,后面再补类型
  3. 从下往上迁移(工具函数 → store → 组件)
  4. 优先给公共 API 加类型,内部实现可以宽松些

Q4:Element Plus 的组件行为和 ElementUI 不一样怎么办?

A

  1. 先查 Element Plus 的 CHANGELOG 和迁移指南
  2. 用全局配置统一调整默认行为
  3. 封装一层自定义组件,抹平差异
  4. 实在不一样的地方,跟产品确认是否接受微调

Q5:重构后性能反而下降了?

A

  1. 检查是否有不必要的响应式数据(用 shallowRef / shallowReactive
  2. 检查 computed 是否有重复计算
  3. 检查大列表是否用了 v-memo 或虚拟滚动
  4. Vue3 的性能优势在大型应用中更明显,小应用可能感知不到

十、总结

重构路线图回顾

Phase 0: 基础设施准备
    ↓
Phase 1: Vue2 → Vue3(兼容模式启动)
    ↓
Phase 2: ElementUI → Element Plus(UI 层统一)
    ↓
Phase 3: Vuex → Pinia(状态管理升级)
    ↓
Phase 4: JavaScript → TypeScript(类型安全)
    ↓
Phase 5: 清理兼容层,全面拥抱 Vue3

关键成功因素

  1. 渐进式:不要追求一步到位,分阶段、按模块迁移
  2. 可回退:每一步都要能回退,小步快跑
  3. 测试保障:有测试就跑测试,没测试就写关键路径的测试
  4. 团队共识:所有人都要了解迁移计划和规范
  5. AI 辅助:用 Claude Code 处理机械性工作,人来做决策和 review

最后一句话

重构不是目的,提升项目可维护性和开发效率才是目的。不要为了用新技术而重构,要因为重构能带来价值而重构。


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

AI砖家

各位大佬,可怜可怜小弟

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值