将项目从 vue2、element-ui 升级到 vue3、element-plus 从技术上具备可行性,但涉及:
-
升级到vue3和element-plus,修改大量代码为新版本语法
-
升级相关依赖,如果没有兼容新版本,则需要替换依赖或执行开发
-
适配样式规范
-
开发自测微服务所有功能
-
测试回归所有功能
操作步骤
升级 vue3
升级 element-plus
vue-cli 5 切换 vite4
echarts 升级 v5
其它依赖升级
步骤详情:
格式化代码
命令行 - ESLint - 插件化的 JavaScript 代码检查工具
npx eslint --ext js,.vue --fix .
npx prettier --write .
全目录替换 tab() 为 4个空格
npx prettier --write .
格式化完成后,充分自测项目并单独提交一次代码,commit 信息中注明代码格式化。
可能遇到的问题:
旧代码不符合 eslint 规范,代码无法提交。需要一一修复 error 问题。
或临时关闭提交前 eslint 检查:删除 package.json 文件中部分内容。(此处修改不提交)

vue2 升级 vue3
gogocode/packages/gogocode-plugin-vue at main · thx/gogocode · GitHub
安装依赖(只需按照一次):
npm install gogocode-cli -g
找到 gogocode 安装目录,通常位于 nodejs 全局目录的 node_modules 文件夹下:


替换 gogocode-plugin-vue 中的 src/api.js 文件:
api.js
替换 gogocode-plugin-vue 中的 src/package-json.js 文件:
package-json.js
转换 vue2 源文件到 vue3,转换后格式化:(建议先根据升级报错处理,处理一下,防止报错)
gogocode -s ./src -t gogocode-plugin-vue -o ./src -p exclude-rules=transition
npx prettier --write .
排除 transition 规则,避免导致scss中注释的格式化问题
使用 gogocode-plugin-vue 转换代码,css样式错乱 · Issue #209 · thx/gogocode · GitHub
Transition 作为根节点 | Vue 3 迁移指南 (vuejs.org)
升级报错处理
![]()
原因:node版本太低,升级到16.18.0
main.js 转换报错处理

修改文件:

gogocode -s ./src/main.js -t gogocode-plugin-vue -o ./src/main.js
格式化成功后,手动修改:

升级时,main.js改动太大。资产main.js中的改动如下,仅供参考
import './util/initGlobal';
import * as Vue from 'vue';
import { createApp } from 'vue';
import App from './App.vue';
import router from './router';
import ElementPlus from 'element-plus';
import i18n from './util/i18n.js';
import VueBus from './util/vueBus.js';
import VueDOMPurifyHTML from 'vue-dompurify-html';
import '@uedm/common/util/randomValue.js';
import echartsXSSPolyfill from './util/echartsXSSPolyfill.js';
echartsXSSPolyfill();
import * as ElementPlusIconsVue from '@element-plus/icons-vue';
import './assets/iconfont/style.css';
import './assets/css/common.scss';
import './assets/css/base.scss';
import './assets/css/dark-theme.scss';
import store from './store';
import './directives';
import './assets/plx-icons/style.css';
if (parent && parent.ZteFrameWork) {
parent.ZteFrameWork.addResponsiveHandler(() => {
app.config.globalProperties.$bus.emit('windowResize');
});
}
import clickoutside from './directives/clickoutside.js';
const app = createApp(App);
window.i18n = i18n;
for (const [key, component] of Object.entries(ElementPlusIconsVue)) {
app.component(key, component);
}
app.config.globalProperties.routerAppend = (path, pathToAppend) => {
return path + (path.endsWith('/') ? '' : '/') + pathToAppend;
};
app.directive('clickoutside', clickoutside);
app.use(VueDOMPurifyHTML, {
default: {
ADD_ATTR: ['target'],
},
});
app.use(i18n);
app.use(ElementPlus);
app.use(VueBus);
app.use(store);
app.use(router);
app.mount('#app');
升级 Element plus
icon预处理问题
1. 找到gogocode的源码,用vscode打开后,找到icon.js

下面的文件替换icon.js内的代码
icon.js
针对<i ref="toggleButton" :class="['el-icon-caret-left', { 'is-active': show }]"></i>的el-icon在:class里面的定义,需要手动找到修改
jsx中的el-icon-,考虑通过增加类名及图片替换,不用plus-icon的写法。增

gogocode -s ./src -t gogocode-plugin-element -o ./src
npx prettier --write .
全局替换 element-ui 为 element-plus
升级 echarts v5(已升级可跳过)
v4 升级 v5 指南 - 版本特性 - 入门篇 - Handbook - Apache ECharts
删除 echarts v4 依赖,保留 echarts v5
替换项目中所有 echarts5 为 echarts
修改 echartsXSSPolyfill.js 文件,去除 echarts5 部分,修改后参考:
echartsXSSPolyfill.js
修改依赖
手动修改:
将 "element-ui": "2.15.1" 修改为 "element-plus": "2.3.3"
删除依赖:"@vue/test-utils": "^1.3.0"
删除 vue-cli 相关依赖
升级依赖:
gogocode -s package.json -t gogocode-plugin-vue -o package.json
安装依赖:
vuex 升级 4.1.0
vue-bus 不支持 vue3,替换为 mitt 3.0.1
vue-dompurify-html 升级 4.1.4
vue-echarts 升级 6.5.4:vue-echarts - npm (npmjs.com)
添加 postcss 依赖
vue-grid-layout 升级 3.0.0-beta1
axios 升级 1.3.4
添加@vitejs/plugin-vue依赖: 4.2.3,
添加@vitejs/plugin-vue-jsx: 3.0.1,
添加@babel/core:7.20.12
依赖修改后参考
package.json
{
"name": "uedm-asset_iui",
"version": "16.18.40",
"private": true,
"scripts": {
"dev": "vite",
"build": "vite build",
"preview": "vite preview",
"lint:quiet": "eslint --quiet",
"stylelint:fix": "stylelint --fix",
"stylelint:quiet": "stylelint --quiet",
"prepare": "cd ../../.. && husky install asset-iui/src/main/.husky",
"lint-staged": "lint-staged"
},
"lint-staged": {
"**/*.{vue,js}": "npm run lint:quiet",
"**/*.{html,vue,css,sass,scss}": "npm run stylelint:quiet"
},
"dependencies": {
"@element-plus/icons-vue": "2.1.0",
"@tweenjs/tween.js": "21.0.0",
"@uedm/common": "1.0.110",
"@uedm/uedm-ui": "0.0.17",
"@wangeditor/editor-for-vue": "5.1.12",
"axios": "1.3.4",
"core-js": "3.30.1",
"crypto-js": "4.1.1",
"d3-zoom": "1.8.3",
"echarts": "5.4.2",
"element-plus": "2.3.3",
"html2canvas": "1.4.1",
"js-file-download": "0.4.12",
"mathjs": "11.8.2",
"mitt": "3.0.1",
"moment": "2.29.4",
"qrcode": "1.5.3",
"uuid": "9.0.0",
"vue": "3.3.4",
"vue-dompurify-html": "4.1.4",
"vue-echarts": "6.5.4",
"vue-grid-layout": "3.0.0-beta1",
"vue-i18n": "9.2.2",
"vue-router": "4.2.4",
"vuex": "4.1.0",
"xss": "1.0.14"
},
"devDependencies": {
"@babel/core": "7.20.12",
"@babel/eslint-parser": "7.19.1",
"@babel/preset-env": "7.20.2",
"@vitejs/plugin-vue": "4.2.3",
"@vitejs/plugin-vue-jsx": "3.0.1",
"autoprefixer": "10.4.13",
"chai": "4.3.7",
"eslint": "8.23.0",
"eslint-config-prettier": "7.2.0",
"eslint-plugin-prettier": "4.2.1",
"eslint-plugin-vue": "9.8.0",
"husky": "8.0.0",
"lint-staged": "12.4.3",
"node-sass": "9.0.0",
"postcss": "8.4.23",
"prettier": "2.7.1",
"sass": "1.56.1",
"sass-loader": "13.3.2",
"stylelint": "15.10.1",
"stylelint-config-recommended-vue": "1.5.0",
"stylelint-config-standard-scss": "9.0.0",
"vite": "4.4.9"
}
}
需要新增的项目(必须)
"dev": "vite",
"build": "vite build",
"vite": "4.4.9",
需要删除的项目:
webpack 及相关依赖

配置 Vite
增加 vite.config.js 文件:
import { defineConfig } from 'vite';
import vue from '@vitejs/plugin-vue';
import vueJsx from '@vitejs/plugin-vue-jsx';
const path = require('path');
// 代理设置
const URL = '10.229.14.199';
const COOKIE =
'BAYEUX_BROWSER=1wck9j6ia3s6k7qy; Z-AUTH-CODE-443=1593882891_d3bdc59b7b454f19a38a5d8d3b0429ac; x-dexmesh-user-443=D86935A99B38A02CDAEA05C225E9303A21FA22B872499884D7B2A1E26A5645FE; forgerydefense-443=1694302c0b8bce2e6bd2b93a895fb8683dac0d0e3af7593e193b8d9363ea7fde';
export default defineConfig({
root: './',
base: './',
publicDir: '/iui/uedm-north-manage/',
build: {
outDir: '../iui/uedm-north-manage',
},
plugins: [
vue(),
vueJsx({
// options are passed on to @vue/babel-plugin-jsx
}),
],
resolve: {
alias: {
'@': path.resolve(__dirname, './src'),
},
extensions: ['.mjs', '.js', '.ts', '.jsx', '.tsx', '.json', '.vue'],
},
define: { 'process.env': {} },
server: {
proxy: {
'^/api/.*': {
// 匹配以 '/api/' 开头的请求。
target: `https://${URL}/`, // 请求转发到服务器
changeOrigin: true,
ws: true,
secure: false,
configure(proxy) {
proxy.on('proxyReq', proxyReq => {
proxyReq.setHeader('Host', URL);
proxyReq.setHeader('Cookie', COOKIE);
proxyReq.setHeader('Referer', `https://${URL}/iui/uedm-battery/`);
proxyReq.setHeader('username', 'admin');
});
},
},
},
},
});
其中:
publicDir: '/iui/uedm-north-manage/',
build: {
outDir: '../iui/uedm-north-manage',
},
路径根据项目具体修改,参考 vue.config.js 文件。
复制 public/index.html 到根目录,并修改:(在原有代码基础上,在<div id="app"></div>下面增加一段<script type="module" src="/src/main.js"></script>)
<div id="app"></div>
<script type="module" src="/src/main.js"></script>
index文件中的
<link rel="icon" href="<%= BASE_URL %>zte_logo_16.gif" />这行修改为
<link rel="icon" href="/zte_logo_16.gif" />
依赖修改完成后,安装依赖,启动开发服务器:
npm install --legacy-peer-deps
npm run dev
根据开发服务器运行报错,修改相关代码。
代码修改
修改 main.js,参考如下:
import './util/initGlobal';
import * as Vue from 'vue';
import App from './App.vue';
import router from './router';
import VueBus from 'vue-bus';
import ElementUI from 'element-plus';
// import ElementLocale from 'element-plus/lib/locale';
import i18n from './util/i18n.js';
// import ECharts from 'vue-echarts/components/ECharts';
import * as echarts from 'echarts';
// 业务中使用 Math.randomValue 替代 Math.random,生成安全的随机数
import '@uedm/common/util/randomValue.js';
// 使用 v-dompurify-html 代替 v-html 解决 xss 问题
import VueDOMPurifyHTML from 'vue-dompurify-html';
// 修复Echarts中的XSS漏洞
import echartsXSSPolyfill from './util/echartsXSSPolyfill.js';
echartsXSSPolyfill();
import 'element-plus/dist/index.css';
import './assets/css/common.scss';
import './assets/css/ajust.scss';
// 黑色主题
import './assets/css/dark-theme.scss';
// 默认主题
import './assets/css/default-theme.scss';
import store from './store';
import 'font-awesome/css/font-awesome.css';
window.$vueApp.use(VueBus);
window.$vueApp.use(VueDOMPurifyHTML);
window.$vueApp.use(ElementUI, { size: 'small', zIndex: 3000 });
// ElementLocale.i18n((key, value) => i18n.t(key, value));
// window.$vueApp.component('Chart', ECharts);
window.$vueApp.config.globalProperties.$echarts = echarts;
window.$vueApp = Vue.createApp(App);
window.$vueApp.config.globalProperties.routerAppend = (path, pathToAppend) => {
return path + (path.endsWith('/') ? '' : '/') + pathToAppend;
};
window.$vueApp.use(i18n);
window.$vueApp.use(store);
window.$vueApp.use(router);
window.$vueApp.mount('#app');
// 本地开发时打开
// window.localStorage.csrftoken = '0b15b316a90d5d7643bbf69734d7bedbfe77f28bf6f9becb9929794f43a14ba9';
其它修改:
-
替换所有 /deep/ 为 ::v-deep,或一步到位,替换为 :deep(),以免控制台一致报警告。正则替换可参考:

-
使用 jsx 的 vue 文件,在 script 标签内添加属性 <script lang="jsx">
-
正则搜索 return.*\([\s\t\n\r]+<
-
搜索 return <
-
-
修改插槽语法及嵌套问题([vite] Internal server error: Codegen node is missing for element/if/for node. Apply appropriate transforms first.)
-
v-slot="scope" 修改为 #default="scope"
-
或者是 slot dom的位置不对
-
v-slot:footer 修改为 #footer
-
el-dialog的内容单独放到一个 vue 文件中,footer 插槽会报这个错。处理:先注释 v-slot:footer 相关,后续处理

-
-
el-button 修改,type="danger" 修改为 type="red",否则会出现删除等按钮红色文字变回主题颜色
-
el-icon 图标问题 Icon 图标 | Element Plus (element-plus.org) 考虑后续用脚本替换,建议图标带 ElIcon 前缀,避免和其它组件名冲突
-

-

-
注意同步替换 模板、import 引入、组件引入三个地方
-
Date、Info、MessageSolid 等图标不存在,需替换为 Calendar、InfoFilled、Comment 等
-
-
修改 i18n.js
-
改为在 main.js 中调用 use
-
js 文件中的 i18n.t() 改为 i18n.global.t()
-
修改 i18n.js 文件,参考如下:
import { createI18n } from 'vue-i18n'; import commonZhLocale from './../lang/common-zh-CN.json'; import commonEnLocale from './../lang/common-en-US.json'; import tenantZhLocale from './../lang/tenant-zh-CN.json'; import tenantEnLocale from './../lang/tenant-en-US.json'; let lang = parent.localStorage['language-option']; let result = (lang && lang.substr(1, lang.length - 2)) || navigator.language || 'zh-CN'; const i18n = new createI18n({ legacy: false, locale: result, globalInjection: true, messages: { 'zh-CN': { ...commonZhLocale, ...tenantZhLocale, }, 'en-US': { ...commonEnLocale, ...tenantEnLocale, }, }, silentTranslationWarn: true, }); export default i18n;
-
-
自定义指令修改 自定义指令 | Vue 3 迁移指南 (vuejs.org)
改为在 main.js 中直接引用
-
修改 require 问题,改为 import 引入
-
i18n.js 中
-
其它文件中的 require
-
-
修改 store/index.js 文件
-
修改创建方式
-
export 到 main.js 中 use
-
-
修改 vue-bus,替换为 mitt。如果未使用,先注释掉。
增加 mitt 依赖:
npm i --save --save-exact mitt@3.0.1替换 vueBus.js 文件为如下内容:
import mitt from 'mitt'; export default function (app) { const emitter = mitt(); emitter.once = (type, handler) => { const fn = (...args) => { emitter.off(type, fn); handler(args); }; emitter.on(type, fn); }; emitter.$emit = emitter.emit; emitter.$on = emitter.on; emitter.$off = emitter.off; emitter.$once = emitter.once; app.config.globalProperties.$bus = emitter; }修改 main.js 中 vueBus 引入方式:
# import * as VueBus from './util/vueBus.js'; # 替换为: import VueBus from './util/vueBus.js';测试功能是否正常。
-
修改App.vue的setSkin方法最后
this.$nextTick(() => {
document.getElementsByTagName('html')[0].className = name;
});
-
vue文件中的样式body.theme-dark需要替换成html.dark
-
其它问题,根据 console 提示修改。
.el-form-item--small需要注意,默认中没有这个class名了
界面显示
可以显示界面,测试相关样式和功能。
参考:
迁移 | Element Plus (element-plus.org)
Element Plus 不兼容变化(中文简体) · element-plus/element-plus · Discussion #5657 · GitHub
gogocode/packages/gogocode-plugin-element at main · thx/gogocode · GitHub
Vue 3 迁移指南 | Vue 3 迁移指南 (vuejs.org)
升级 Vue3 的最后一块拼图,快试试这个工具自动升级你的 Element 老项目 - 掘金 (juejin.cn)
Migrate from v4 | Vue CLI (vuejs.org)
GoGoCode | 代码魔法师 代码转换工具
过程问题记录
可以 运行编译项目,但报错,i18n.js 问题。

i18n 升级 v9
升级 i18n:
npm install vue-i18n@9
修改 i18n 引入路径
如何从 Vue CLI 迁移到 Vite - 掘金 (juejin.cn)
vite 启动后 404:
把 index.html 文件从 public 目录下移到根目录;

修改解析后缀名

转换后的问题,没有换行,进行全局替换


jsx不支持
修改对应文件的 script标签,添加 lang="jsx"
![]()

修改 element-ui 为 element-plus
修改 i18n 引入
修改 echarts

vue2 升级 vue3 转换后部分注释scss代码问题,可以全局替换,添加换行

注意:升级后v-loading 的div和overflow: auto的div不能为同一个div,否则滚动条在loading之后会消失!

element-plus转换后,默认时间改变,并添加了 dayjs 依赖

elementPlus中不支持el-date-picker组件的pickerOptions属性。
如遇以上报错,搜索<el-date-picker,修改为直接使用disabledDate属性。

替换所有 /deep/ 为 ::v-deep

v-slot插槽使用问题
【compiler-core】为什么插槽混用会导致作用域不明确的问题?会导致编译阶段报错,且报错内容难以定位是插槽混用导致的 · Issue #5807 · vuejs/core · GitHub
插槽语法:
v-slot 改为 #default
v-slot:footer 改为 #footer
[plugin:vite:import-analysis] Failed to resolve import "element-plus/lib/locale" from "src\main.js"

删除 ElementLocale,改为直接引入 i18n

自定义指令加载报错。改为在 main.js 文件中全局引入。

删除 vue 文件中的 util 引用

添加 export default

修改 require 为 import


vue-slider-component 报错:
npm install vue-slider-component@next --save

vite.config.js 中定义 process.env:


main.js 中 vue初始化顺序问题
window.$vueApp.mount('#app') 必须在最后才挂载
vite 中配置后端接口
配置server.proxy

正则配置问题:

接口报500:

proxy 增加配置 secure: false,可以访问。
报错 501:

路径错误,target 中去掉 api/
界面可以正常运行,可以提交请求。

项目启动报错

npm install postcss-pxtorem -D
vite build

构建时,如果构建目录没有在源代码根目录下,则不会清空目标目录下的文件,并出现警告:

需要在build后增加参数--emptyOutDir

| 报错 | |
|---|---|
Failed to resolve import "@element-plus/icons" from "src\components\tree\AlarmCodeTree.vue" | 安装依赖: @element-plus/icons-vue |
| Catch all routes ("*") must now be defined using a param with a custom regexp. | path: '*',改为path: '/:catchAll(.*)', |
| Cannot read properties of undefined (reading 't') js中this.$i18n.t报错 | 修改为window.i18n.global.t |
| core-base.esm-bundler.js:1185 Message compilation error: Invalid linked format
| vue-i18n的9以上版本中@被用作特殊字符处理 把@改为{'@'} |
| <link rel="icon" href="<%= BASE_URL %>zte_logo_16.gif" /> 报错[vite] Internal server error: URI malformed | <link rel="icon" type="image/svg+xml" href="/public/zte_logo_16.gif" /> |
| VsCode 报 v-model directives require no argument 警告 | 在.eslintrc.js中增加'vue/no-v-model-argument': 'off', |
| VsCode 报 The template root requires exactly one element | 在.eslintrc.js中增加'vue/no-multiple-template-root': 0, |
| VsCode 报 Parsing error: This experimental syntax requires enabling one of the following parser plugin(s): "jsx", "flow", "typescript". | 在.eslintrc.js中增加配置:
|
filters 筛选语法vue3不支持


原来写在filters中的方法会加上_filter的后缀写在method里面,需要手动改方法名, 其中的$t.t改为this.$t
待解决问题
样式适配
vite开启 https
组态升级问题?
迁移 Pinia
vuex最后更新于2021年6月,马上超出2年,需要替换成 pinia
从 Vuex ≤4 迁移 | Pinia (vuejs.org)
4.1.0 发布于2022.10
| 例子 | |
|---|---|
| vue2中执行多个表单校验后可以正常获得校验结果,vue3中不行,需要处理异步返回问题 | 北向northServiceManage.vue中handleConfirm方法的else分支 |
| vue3中修改了表单校验的rules的属性,会自动触发表单校验,清楚表单校验需要增加延时 | 北向addFtpServers.vue中的mounted最后 |
样式产生的问题
1. 表单中 el-popper , 着重检查下拉框包含一棵树的表单

参考方法:

本文详细记录了一个项目从Vue2和Element-UI升级到Vue3和Element-Plus的过程,包括升级步骤、遇到的问题及解决办法。涉及Vue3的代码转换、依赖升级、Echarts V5、Vite配置、代码修改等多个方面,同时列举了在样式、i18n、自定义指令等方面的适配问题及其解决方案。


1439

被折叠的 条评论
为什么被折叠?



