背景:你是前端,日常业务 Vue2,考虑升级,分为两种路径:完整迁移(整个项目改成 Vue3 + Vite)、渐进式兼容(vue2‑bridge,混合模式)。Vue2 已经停止维护,长远建议迁移;但业务项目优先评估业务风险。
一、核心变化总览
- 构建工具:
webpack→ Vite(推荐),也可以继续 webpack5 - 框架:
vue 2.x→vue 3.x - 语法:选项式API可以继续用;推荐组合式API
<script setup> - 库:
vue-router@3→vue‑router@4vuex@3→ pinia(优先,vuex还能用但不再迭代)element‑ui→element‑plus
重点:ElementUI 不兼容 Vue3,必须替换 Element‑Plus。
二、方案A:渐进迁移(风险最低,适合老业务大项目)
使用 @vue/compat 兼容构建模式,Vue3可以跑大部分Vue2代码,逐步改造,不用一次性全部改完。
适合:业务复杂、迭代不能停、不能一次性全量重构。
- 安装依赖
npm install vue@3 @vue/compat
npm install -D @vitejs/plugin-vue vite
- vite配置开启兼容模式
// vite.config.js
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
export default defineConfig({
plugins: [
vue({
template: {
compilerOptions: {
compatConfig: { MODE: '2' }
}
}
})
]
})
- main.js改动
// Vue2 旧代码
// import Vue from 'vue'
// new Vue({el:'#app',render:h=>h(App)})
// Vue3 compat模式
import { createApp } from 'vue'
import App from './App.vue'
const app = createApp(App)
app.mount('#app')
⚠️ compat只是过渡,会有警告,逐个修复警告,逐步把组件改成标准Vue3写法,最后移除compat。
三、方案B:完整迁移(新项目/小项目,彻底Vue3)
1. 破坏性改动高频清单(你改项目最常踩坑)
- 实例this
Vue3 setup 里面没有this,改用ref / reactive。
<script setup>
import {ref} from 'vue'
const count = ref(0)
</script>
- 生命周期
beforeDestroy→beforeUnmountdestroyed→unmounted - 事件:
$listeners被移除,v‑on直接透传 - v‑if 和 v‑for 优先级调换:Vue2 v‑for优先;Vue3 v‑if优先,不要写在同一标签。
- 过滤器 filters 整个移除,不能用,改成方法或者计算属性。
// ❌ filters: { format(){}}
// ✅ computed / function
- 组件 v‑model 改动
<!-- vue2 -->
<Child v-model="val"/>
<!-- vue3 默认 -->
<Child v-model="val"/>
<!-- 本质是 :modelValue + @update:modelValue -->
- 双向绑定
.sync语法删除,全部替换成v‑model:xxx - 全局API变更
// vue2
// Vue.prototype.$xxx = xxx
// vue3
app.config.globalProperties.$xxx = xxx
- 虚拟DOM:根节点不再限制单根,template可以多个根元素。
2. 周边库替换对照表
| Vue2 | Vue3 |
|---|---|
| vue‑router@3 | vue‑router@4 |
| vuex@3 | pinia(推荐) |
| element‑ui | element‑plus |
| axios | 不变 |
3. main.js完整示例
import { createApp } from 'vue'
import App from './App.vue'
import router from './router'
import { createPinia } from 'pinia'
import ElementPlus from 'element-plus'
import 'element-plus/dist/index.css'
const app = createApp(App)
app.use(router)
app.use(createPinia())
app.use(ElementPlus)
app.mount('#app')
四、迁移实操步骤(工程落地顺序)
- 备份代码,拉独立迁移分支,不要直接改主分支
- 升级/替换核心依赖 vue、router、UI库
- 修复构建报错:filters、.sync、生命周期名称、v‑for+v‑if
- 解决UI组件API差异(element‑ui → element‑plus很多属性名字变)
- 修复全局
Vue.prototype→globalProperties - 跑单元测试,手工全量回归业务页面
- 逐步把组件从选项式API改
<script setup>(可选,不改也可以跑)
五、现实业务建议
- 如果是外包老项目,业务很重:优先compat渐进模式,不要一次性大改,大改容易出线上bug,甲方也不希望无业务改动大规模重构。
- 小页面、新页面直接写
<script setup>,旧页面保留选项式慢慢迭代。 - 不要一上来就全部重写业务逻辑,优先保证功能跑通,再优化写法。
六、常见坑
- element‑ui组件很多props改名,比如
size="small"部分、弹窗事件、表单校验规则; - 第三方旧组件库,很多不支持Vue3,要找vue3版本替代;
- webpack迁移Vite:部分webpack特有插件需要替换,别名配置写法略有差异。
另附
Vue2 + Element‑UI → Vue3 + Vite + Element‑Plus 完整迁移实操
适用:把原有 webpack‑vue2‑element‑ui 项目,完整迁移到 Vite+Vue3+Element‑Plus,不是兼容模式,彻底升级。
⚠️重要:不要直接在main分支改,新建迁移分支,原有代码做好备份。
1、新建vite基础项目(参考)
# 创建vite‑vue3模板
npm create vite@latest my‑vue3‑proj -- --template vue
cd my‑vue3‑proj
npm install
# 安装周边
npm install vue-router@4 pinia element-plus axios
npm install -D unplugin-vue-components unplugin-auto-import
unplugin‑xxx自动导入element‑plus组件,不用每个页面手动import,生产项目必用。
vite.config.js
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import { resolve } from 'path'
import AutoImport from 'unplugin-auto-import/vite'
import Components from 'unplugin-vue-components/vite'
import { ElementPlusResolver } from 'unplugin-vue-components/resolvers'
export default defineConfig({
plugins: [
vue(),
AutoImport({
resolvers: [ElementPlusResolver()],
imports: ['vue','vue-router']
}),
Components({
resolvers: [ElementPlusResolver()]
})
],
resolve:{
alias:{
'@': resolve(__dirname,'./src')
}
},
server:{
port:8080
}
})
2、main.js 新旧对比
Vue2 webpack main.js(旧)
import Vue from 'vue'
import ElementUI from 'element-ui'
import 'element-ui/lib/theme-chalk/index.css'
import App from './App.vue'
import router from './router'
import store from './store'
Vue.use(ElementUI)
new Vue({
router,
store,
render: h => h(App)
}).$mount('#app')
Vue3 Vite main.js(新)
import { createApp } from 'vue'
import App from './App.vue'
import router from './router'
import { createPinia } from 'pinia'
// 如果不用自动导入插件,才需要全局引入element‑plus
// import ElementPlus from 'element-plus'
// import 'element-plus/dist/index.css'
const app = createApp(App)
app.use(router)
app.use(createPinia())
// app.use(ElementPlus)
app.mount('#app')
建议优先使用上面
unplugin自动导入,减少打包体积。
3、路由 router/index.js
vue‑router@4 写法变化
import { createRouter, createWebHistory } from 'vue-router'
const routes = [
{ path:'/', component:()=>import('@/views/home.vue') }
]
const router = createRouter({
history: createWebHistory(),
routes
})
export default router
❌不再使用
new VueRouter()
4、状态管理:vuex → pinia
vuex不再推荐,迁移pinia。
store示例 src/stores/user.js
import { defineStore } from 'pinia'
export const useUserStore = defineStore('user',{
state:()=>({
name:'',
token:''
}),
actions:{
setToken(val){
this.token = val
}
}
})
5、业务组件迁移重点(高频踩坑)
5‑1 生命周期改名
beforeDestroy → beforeUnmount
destroyed → unmounted
5‑2 移除 filters
vue3没有过滤器,改用 computed 或者页面内方法。
5‑3 .sync 全部删除,替换 v‑model:属性名
<!-- vue2 -->
<el-dialog :visible.sync="showDialog"/>
<!-- vue3 -->
<el-dialog v-model:visible="showDialog"/>
5‑4 v‑if 和 v‑for
vue3中 v‑if优先级高于v‑for,禁止写同一个标签;放到外层容器分开。
5‑5 this 问题
<script setup> 里面没有this;
选项式API还可以继续用this。
<script setup>
import { ref,reactive } from 'vue'
const showDialog = ref(false)
</script>
5‑6 全局挂载
// vue2
// Vue.prototype.$api = api
// vue3
app.config.globalProperties.$api = api
5‑7 ElementUI → ElementPlus 重要改动
- 弹窗
this.$alert / this.$confirm
// vue2
this.$confirm('确定?')
// vue3 script‑setup中
import { ElMessageBox } from 'element-plus'
ElMessageBox.confirm('确定?')
- 表单校验:
this.$refs.form.validate()用法大体不变,部分prop改名
size枚举值变化el‑table部分事件、属性变更;sort‑change、selection‑change 事件名不变,但参数细节有差异- el‑input
value‑sync→v‑model
- 图标:element‑plus图标单独包
@element‑plus/icons‑vue,需要单独安装导入
npm install @element-plus/icons-vue
6、迁移落地实操步骤(真实项目)
- 创建vite‑vue3新项目,搭建基础架子:router、pinia、element‑plus自动导入
- 把旧项目src下面:api、utils、assets、views组件拷贝过来
- 逐个修复报错:
- .sync语法
- filters过滤器
- beforeDestroy生命周期
- element‑ui组件属性、弹窗调用方式
- 替换图标依赖
- 路由检查,404、history模式
- 本地完整跑一遍所有业务页面,回归表单、弹窗、表格、分页
- 处理环境变量:
- webpack
process.env→ vite使用import.meta.env
- 打包测试
npm run build
⚠️老项目如果大量第三方组件,要看是否支持vue3;不支持要找替代。
7、两种组件写法选择
- 选项式API(和vue2几乎一样),可以继续写,不用全部改成setup,适合快速迁移:
<script>
export default {
data(){return {}},
methods:{}
}
</script>
- 是推荐新写法,新页面优先用,老页面可以不改。
- 8、风险提醒
- 如果是线上业务不能停,不建议一次性整体大迁移;可以考虑新建项目,业务页面分批迁移。
- elementUI 到 ElementPlus 有很多细节 UI 差异,表格、弹窗、校验,需要人工回归,会有不少隐性 bug。
- 如果你需要,我可以给你一份:一个完整 vue2 选项式组件,转成
<script setup>的对照示例。
