vue2升级到vue3

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

将项目从 vue2、element-ui 升级到 vue3、element-plus 从技术上具备可行性,但涉及:

  1. 升级到vue3和element-plus,修改大量代码为新版本语法

  2. 升级相关依赖,如果没有兼容新版本,则需要替换依赖或执行开发

  3. 适配样式规范

  4. 开发自测微服务所有功能

  5. 测试回归所有功能

操作步骤

升级 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 文件中部分内容。(此处修改不提交)

image.png

vue2 升级 vue3

gogocode/packages/gogocode-plugin-vue at main · thx/gogocode · GitHub

安装依赖(只需按照一次):

npm install gogocode-cli -g

找到 gogocode 安装目录,通常位于 nodejs 全局目录的 node_modules 文件夹下:

image.png

image.png

替换 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)

升级报错处理

image.png

原因:node版本太低,升级到16.18.0

main.js 转换报错处理

image.png

修改文件:

image.png

gogocode -s ./src/main.js -t gogocode-plugin-vue -o ./src/main.js

格式化成功后,手动修改:

image.png

升级时,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

image.png

下面的文件替换icon.js内的代码

icon.js

针对<i ref="toggleButton" :class="['el-icon-caret-left', { 'is-active': show }]"></i>的el-icon在:class里面的定义,需要手动找到修改

jsx中的el-icon-,考虑通过增加类名及图片替换,不用plus-icon的写法。增

image.png

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 及相关依赖

image.png

配置 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';

其它修改:

  1. 替换所有 /deep/ 为 ::v-deep,或一步到位,替换为 :deep(),以免控制台一致报警告。正则替换可参考:

    image.png

  2. 使用 jsx 的 vue 文件,在 script 标签内添加属性 <script lang="jsx">

    1. 正则搜索 return.*\([\s\t\n\r]+<

    2. 搜索 return <

  3. 修改插槽语法及嵌套问题([vite] Internal server error: Codegen node is missing for element/if/for node. Apply appropriate transforms first.)

    1. v-slot="scope" 修改为 #default="scope"

    2. 或者是 slot dom的位置不对

    3. v-slot:footer 修改为 #footer

    4. el-dialog的内容单独放到一个 vue 文件中,footer 插槽会报这个错。处理:先注释 v-slot:footer 相关,后续处理

      image.png

  4. el-button 修改,type="danger" 修改为 type="red",否则会出现删除等按钮红色文字变回主题颜色

  5. el-icon 图标问题  Icon 图标 | Element Plus (element-plus.org)  考虑后续用脚本替换,建议图标带 ElIcon 前缀,避免和其它组件名冲突

    1. image.png

    2. image.png

    3. 注意同步替换 模板、import 引入、组件引入三个地方

    4. Date、Info、MessageSolid 等图标不存在,需替换为 Calendar、InfoFilled、Comment 等

  6. 修改 i18n.js

    1. 改为在 main.js 中调用 use

    2. js 文件中的 i18n.t() 改为 i18n.global.t()

    3. 修改 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;

  7. 自定义指令修改  自定义指令 | Vue 3 迁移指南 (vuejs.org)

    改为在 main.js 中直接引用

  8. 修改 require 问题,改为 import 引入

    1. i18n.js 中

    2. 其它文件中的 require

  9. 修改 store/index.js 文件

    1. 修改创建方式

    2. export 到 main.js 中 use

  10. 修改 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';

    测试功能是否正常。

  11. 修改App.vue的setSkin方法最后

    this.$nextTick(() => {

                    document.getElementsByTagName('html')[0].className = name;

     });

  12. vue文件中的样式body.theme-dark需要替换成html.dark

  13. 其它问题,根据 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 问题。

image.png

i18n 升级 v9

升级 i18n:

npm install vue-i18n@9

修改 i18n 引入路径

如何从 Vue CLI 迁移到 Vite - 掘金 (juejin.cn)

vite 启动后 404:

把 index.html 文件从 public 目录下移到根目录;

image.png

修改解析后缀名

image.png

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

image.png

image.png

jsx不支持

修改对应文件的 script标签,添加 lang="jsx"

image.png

image.png

修改 element-ui 为 element-plus

修改 i18n 引入

修改 echarts

image.png

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

image.png

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

image.png

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

    

elementPlus中不支持el-date-picker组件的pickerOptions属性。

如遇以上报错,搜索<el-date-picker,修改为直接使用disabledDate属性。

image.png

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

image.png

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"

image.png

删除 ElementLocale,改为直接引入 i18n

image.png

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

image.png

删除 vue 文件中的 util 引用

image.png

添加 export default

image.png

修改 require 为 import

image.png

image.png

vue-slider-component 报错:

npm install vue-slider-component@next --save

image.png

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

image.png

image.png

main.js 中 vue初始化顺序问题

window.$vueApp.mount('#app') 必须在最后才挂载

vite 中配置后端接口

配置server.proxy

image.png

正则配置问题:

image.png

接口报500:

image.png

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

报错 501:

image.png

路径错误,target 中去掉 api/

界面可以正常运行,可以提交请求。

image.png

项目启动报错

image.png

npm install postcss-pxtorem -D

vite build

image.png

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

image.png

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

image.png

报错
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

image.png

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中增加配置:

image.png

filters  筛选语法vue3不支持

image.png

image.png

原来写在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 ,  着重检查下拉框包含一棵树的表单

image.png

参考方法: 

image.png

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值