VUE2_TO_VITE_VUE3
Vue 2 项目迁移至 Vite + Vue 3 实战指南
项目背景
aiportal-ui原本是 Vue 2 项目,现在运行在Vite + Vue 3.5.34环境,但可以使用Vue 2 语法编写组件。
一、核心配置要点
1.1 vite.config.mjs - 启用 Options API
define:{__VUE_OPTIONS_API__:true,// ← 关键:启用 Vue 2 Options API 风格__VUE_PROD_DEVTOOLS__:false,},原理:Vue 3 默认关闭 Options API,通过这个全局变量启用它。
1.2 package.json - 依赖版本
{"vue":"^3.5.34","view-ui-plus":"^1.3.24",// ← Vue 3 版本的 iView"element-plus":"^2.11.1","vuex":"^4.1.0",// ← Vue 3 版本的 Vuex"vue-router":"^4.5.1","mitt":"^3.0.1"// ← 替代 Vue 2 的 $bus}二、兼容性实现三大机制
2.1 机制一:mitt 替代 $bus(事件总线)
文件:src/main.js
importmittfrom"mitt";constemitter=mitt();constbus={$on:emitter.on,$off:emitter.off,$emit:emitter.emit,on:emitter.on,off:emitter.off,emit:emitter.emit,};// 挂载到全局app.config.globalProperties.$bus=bus;使用方式(Vue 2 风格):
// 组件中直接使用this.$bus.$on('eventName',this.handler)this.$bus.$emit('eventName',data)this.$bus.$off('eventName',this.handler)2.2 机制二:ui-compat.js(UI 组件兼容层)
文件:src/plugins/ui-compat.js
import{h}from"vue";import{ElMessage,ElMessageBox,ElNotification}from"element-plus";// 将 Element Plus 的 API 适配成 Vue 2 风格exportconstMessage=(options)=>ElMessage(options);Message.success=(options)=>ElMessage.success(...);Message.warning=(options)=>ElMessage.warning(...);Message.error=(options)=>ElMessage.error(...);Message.info=(options)=>ElMessage.info(...);exportconstModal={info(options={}){...},confirm(options={}){...},};exportconstNotice={open(options){returnElNotification(options);},success(options){...},// ...};// 安装函数exportfunctioninstallUiCompat(app){app.config.globalProperties.$Message=Message;app.config.globalProperties.$Modal=Modal;app.config.globalProperties.$Notice=Notice;app.component("Icon",IconCompat);// 自定义 Icon 组件}使用方式(Vue 2 风格):
this.$Message.success('操作成功')this.$Modal.confirm({content:'确定删除?'})this.$Notice.warning({content:'警告'})2.3 机制三:view-ui-plus-ns.js(iView 组件兼容)
文件:src/plugins/view-ui-plus-ns.js
import*asViewUIPlusfrom"view-ui-plus";functionisIViewAliasName(name){return/^i[A-Z]/.test(name);// 检测 i 前缀}functionremoveIViewPrefix(name){returnisIViewAliasName(name)?name.slice(1):name;}exportfunctioninstallViewUiPlusNs(app){Object.entries(ViewUIPlus).forEach(([,component])=>{// 自动注册:去除 i 前缀(如 iButton -> Button)constnormalizedName=removeIViewPrefix(name);app.component(normalizedName,component);});// 全局方法app.config.globalProperties.$Message=ViewUIPlus.Message;app.config.globalProperties.$Notice=ViewUIPlus.Notice;app.config.globalProperties.$Modal=ViewUIPlus.Modal;}使用方式(Vue 2 风格):
<template><Button@click="handleClick">点击</Button><Breadcrumb><BreadcrumbItem>首页</BreadcrumbItem></Breadcrumb><Card>内容</Card></template>三、main.js 完整初始化流程
import{createApp}from"vue";importElementPlusfrom"element-plus";importzhCnfrom"element-plus/es/locale/lang/zh-cn";import"element-plus/dist/index.css";import"view-ui-plus/dist/styles/viewuiplus.css";importmittfrom"mitt";importAppfrom"./App.vue";importrouterfrom"./router";importstorefrom"./store";import{installUiCompat}from"./plugins/ui-compat";import{installViewUiPlusNs}from"./plugins/view-ui-plus-ns";importi18nfrom"../i18n/index";constapp=createApp(App);// 1. Element Plus(Vue 3 组件库)app.use(ElementPlus,{locale:zhCn});// 2. 自定义兼容层(Message/Modal/Notice/Icon)installUiCompat(app);// 3. View UI Plus(Vue 3 版本的 iView)installViewUiPlusNs(app);// 4. Vuex(Vue 3 版本)app.use(store);// 5. Vue Routerapp.use(router);// 6. Vue I18napp.use(i18n);// 7. 全局混入会添加 handleError 方法app.mixin({methods:{handleError(msg,err){...}}});// 8. 事件总线constemitter=mitt();app.config.globalProperties.$bus={$on,$off,$emit,on,off,emit};// 9. 挂载app.mount("#app");四、关键差异对比
| 特性 | Vue 2 | Vue 3 兼容方式 |
|---|---|---|
| 事件总线 | Vue.prototype.$bus | mitt+globalProperties |
$Message | iView/Element UI | ui-compat.js适配层 |
$Modal | iView/Element UI | ui-compat.js适配层 |
$Notice | iView/Element UI | ui-compat.js适配层 |
| 组件命名 | <i-Button> | 自动去除 i 前缀<Button> |
| Vuex | Vuex | vuex@4+app.use(store) |
| 路由 | VueRouter@3 | vue-router@4 |
五、迁移检查清单
如果要将一个 Vue 2 项目迁移到 Vite + Vue 3:
- package.json:升级
vue、vue-router、vuex到 v4 版本 - vite.config.mjs:添加
define: { __VUE_OPTIONS_API__: true } - main.js:改用
createApp()而非new Vue() - 事件总线:引入
mitt替代$bus - Message/Modal/Notice:创建兼容层适配新组件库
- Icon 组件:检查并创建兼容实现
- [第三方组件库:确保使用 Vue 3 版本(如
view-ui-plus而非iview)
六、文档路径
- 示例项目:
D:\benchmarkui\portal\aiportal-ui - 兼容层:
src/plugins/ui-compat.js、src/plugins/view-ui-plus-ns.js - 入口文件:
src/main.js - 构建配置:
vite.config.mjs
七、核心配置要点
1.1 vite.config.mjs - 完整的
import{defineConfig}from"vite";importvuefrom"@vitejs/plugin-vue";importpathfrom"path";import{fileURLToPath}from"url";import{viteStaticCopy}from"vite-plugin-static-copy";importbasicSslfrom"@vitejs/plugin-basic-ssl";const__filename=fileURLToPath(import.meta.url);const__dirname=path.dirname(__filename);exportdefaultdefineConfig({plugins:[basicSsl(),vue({template:{},}),viteStaticCopy({targets:[{src:path.resolve(__dirname,"static/**/*"),dest:"static",},],}),],// @element-plus/icons-vue 预构建时会直接从 vue 内部 chunk 拉 defineComponent,// 绕过了 vue 入口的 init_runtime_dom,导致 isFunction 未初始化即调用 defineComponent。optimizeDeps:{exclude:["@element-plus/icons-vue"],},resolve:{extensions:[".mjs",".js",".ts",".jsx",".tsx",".json",".vue"],alias:{"@common":path.resolve(__dirname,"../common"),"@":path.resolve(__dirname,"src"),"@U":path.resolve(__dirname,"src/utils"),"@P":path.resolve(__dirname,"static"),uuid:path.resolve(__dirname,"node_modules/uuid/dist/index.js"),},},define:{__VUE_OPTIONS_API__:true,__VUE_PROD_DEVTOOLS__:false,},server:{host:"ai.test.huawei.com",port:8080,https:true,proxy:{"/rest":{target:"https://platform-dev.ai.test.huawei.com/",ws:false,changeOrigin:true,secure:false,},},},build:{outDir:path.resolve(__dirname,"public","html"),emptyOutDir:true,},});原理:Vue 3 默认关闭 Options API,通过这个全局变量启用它。
package.json调用:
{"name":"data-management-ui","version":"0.1.0","private":true,"type":"module","scripts":{"serve":"vite --mode development","serve-uat":"vite --mode uat","build-prd":"node --max_old_space_size=8192 node_modules/vite/bin/vite.js build --mode production","build-uat":"node --max_old_space_size=8192 node_modules/vite/bin/vite.js build --mode uat","build-beta":"node --max_old_space_size=8192 node_modules/vite/bin/vite.js build --mode beta","build-dev":"node --max_old_space_size=8192 node_modules/vite/bin/vite.js build --mode dev","lint":"eslint \"src/**/*.{js,vue}\" --fix","i18n:check":"node scripts/check-i18n-keys.mjs"},