[Front-end vue upgrade] vue2+js+elementUI upgrade to vue3+ts+elementUI plus

Time:2023-10-8

I. Selection of tools

Recently I wanted to upgrade my vue+js+elementUI project to vue3+ts+elementUI plus for a better development experience, and vue3 has significantly improved performance, so I'm going to document the upgrade process here
 Manual replacement is certainly not an achievable solution for a project in use. A better solution would be to deconstruct the code based on an AST (Abstract Syntax Tree), modify it in batches according to the established rules, and then output the files.
 Currently the mainstream conversion tools are roughly babel plug-in, gogo code, jscodeshift, etc., in contrast to the official documentation, the way of use and other aspects of the final decision to use the gogo code to facilitate the upgrade. (The official documentation is more clear in comparison, the use of more convenient, for some code conversion core code is more concise)

gogo code is a JavaScript/Typescript/HTML code conversion tool based on AST (Abstract Syntax Structure Tree Representation of Source Code), which can be used to build a code conversion program to help automate tasks such as framework upgrades, code refactoring, and multi-platform conversions.
Currently GoGoCode supports parsing and manipulating the following types of code:
○JavaScript(JSX)
○Typescript(TSX)
○HTML
○Vue

II. Upgrade-related dependencies

1. Use the gogo code

npm install gogocode-cli -g

2. Migration of source files

gogocode -s ./src -t gogocode-plugin-vue -o ./src-out
// . /src is the destination directory, . /src-out is the output directory, and if they are the same, they will overwrite the

3. Upgrade dependencies

gogocode -s package.json -t gogocode-plugin-vue -o package.json

4. Upgrade elementUI

gogocode -s ./src -t gogocode-plugin-element  -o ./src
// elementUI does not support vue3, you need to upgrade to elementUI plus.

/Icon related displays still need to be adapted manually

//Code with syntax errors in the case of conversion will be lagging, need to be handled manually

5. Download related dependencies

npm install

6. Precautions before upgrading
(1) Conversion may be stalled, you need to troubleshoot the stuck files and delete them temporarily so that the conversion will be successful;
2) Code conversion will be done according to gogocode’s built-in style, which may change the code style of the original code;
3) Also, because gogocode is still in the iterative process, different versions can cause differences in the effectiveness of the upgrade;

7. Advantages and disadvantages of tools
(1) the use of the tool can reduce the upgrade workload, but at the same time will produce some other unknown pitfalls, the use of the process needs to be converted to the code for troubleshooting. Currently for large projects or prioritize the manual upgrade
2) If the tool is not used for one-click conversion, the tool has separate desirable features, such as: automatically generated event API proxy file and package.json changes, etc.

8. Notes after upgrading
Individual sections still need to be modified manually, for example:
1) router’s match all paths
2) /deep/.class in style needs to be manually changed to deep(.class)
3) Global app

// Since the Vue3 global object becomes created by createApp(), the conversion tool passes the object returned by createApp() to window.$vueApp. You need to move the window.$vueApp = Vue.createApp(App) block to the top of the call to window.$vueApp code. at the top of the code that calls window.$vueApp. This prevents window.$vueApp from being undefined.

[Front-end vue upgrade] vue2+js+elementUI upgrade to vue3+ts+elementUI plus

9. The general process of tool compilation
[Front-end vue upgrade] vue2+js+elementUI upgrade to vue3+ts+elementUI plus

1. Parsing code into an abstract syntax tree (AST)
2. Find the code that needs to be changed
3. Modify it to the desired look
4. Then generate the code back to the string form

III. Error message

  1. First error.
    [Front-end vue upgrade] vue2+js+elementUI upgrade to vue3+ts+elementUI plus

Reason: Dependency versions do not correspond
Solution: Uninstall less-loader, webpack and reinstall it.

  1. Second error.
    [Front-end vue upgrade] vue2+js+elementUI upgrade to vue3+ts+elementUI plus

Cause:ValidationError:Invalid option object. A development server has been initialized with an option object that does not match the API architecture.
In layman’s terms, this means that a property is used that is not compatible with the current release
Solution: Remove the non-adaptation attribute in vue.config.js as indicated in the error report
[Front-end vue upgrade] vue2+js+elementUI upgrade to vue3+ts+elementUI plus

Next the project is running, just change the corresponding error message

1、eslint-plugin-vue
[Front-end vue upgrade] vue2+js+elementUI upgrade to vue3+ts+elementUI plus

Solution: Redownload ‘eslint-plugin-vue’.

2、yarn
[Front-end vue upgrade] vue2+js+elementUI upgrade to vue3+ts+elementUI plus

Solution: remove yarn.lock (or re-download it if you need yarn)

3、Match all routes
[Front-end vue upgrade] vue2+js+elementUI upgrade to vue3+ts+elementUI plus

Reason: Matching all routes in vue2 can be done using path:*, in vue3 you need to change the path as follows

{
// Match all paths vue2 uses vue3 uses /:pathMatch(.) or / catchAll(. *)
  path: "/:pathMatch(.)",
   name: "404",
   component: ()=> import("../components/NotFound.vue")
 }

IV. Code-related changes
1、Route matching but page 404
When a page has optional parameters, in the vue2 router you can write two[Front-end vue upgrade] vue2+js+elementUI upgrade to vue3+ts+elementUI plus

New in vue3 is the optimization of optional parameters, just add a question mark after the parameter[Front-end vue upgrade] vue2+js+elementUI upgrade to vue3+ts+elementUI plus

const routes = [
  // Matches /users and /users/posva
  { path: '/users/:userId?' },
  // Matches /users and /users/42
  { path: '/users/:userId(\\d+)?' },
]
// * can also mean that the argument is optional, but ? parameters cannot be duplicated.

// Repeatable parameters
const routes = [
  ///:chapters -> Matching /one, /one/two, /one/two/three, etc
  { path: '/:chapters+' },
  // // : chapters - > matching, / one/one/two/one/two/three, and so on
  { path: '/:chapters*' },
]
// *: 0 or more, +: 1 or more

// or use a regular approach to implement repeatable parameters
const routes = [
  // Match numbers only
  // Match /1, /1/2, etc.
  { path: '/:chapters(\\d+)+' },
  // Matches /, /1, /1/2, etc.
  { path: '/:chapters(\\d+)*' },
]

2, vue2 can el-col nested el-col, in vue3 can not, must be el-row nested
[Front-end vue upgrade] vue2+js+elementUI upgrade to vue3+ts+elementUI plus

3、main.js
When mounting a method on the prototype chain in vue2 you can use Vue.prototype.xxx directly and use it with this

import Vue from 'vue';
Vue.prototype.$loader = loader;

this.$loader.post....

This has been modified in vue3 to read

import App from './App.vue';
import { createApp } from 'vue';
const app = createApp(App);
app.config.globalProperties.$loader = loader;

//When used
<script setup lang="ts">
  import { getCurrentInstance } from 'vue'
  const { proxy }: any = getCurrentInstance()
  console.log('proxy:', proxy)
  console.log('getAction:', proxy.$getAction)
</script>
// This can also be used in js with this.xxx
// If a global property conflicts with a component's own property, the component's own property will have higher priority

4、Parent-child component pass value
1)vue2
Since vue is a unidirectional data stream, parent-child components that want to achieve the effect of v-model when passing values will need to use the .sync modifier to achieve “two-way binding”.

<DialogContent
  :title.sync="addDialogTitle"
/>

2)vue3
The v-model has been revamped so that the .sync modifier is no longer needed to achieve two-way data binding. In vue3, multiple v-model properties are supported. By default, modelValue is used as the prop, update title=”title” is used as the prop, and update:title is used as the event.

<DialogContent
  v-model:title="addDialogTitle"
  v-model:Name="addName"
/>

5. Root node
There can only be one root node in template in vue2

<template>
  <div id="app">
  	...
  </div>
</template>

Multiple root nodes can exist in vue3

<template>
  <div>...</div>
  <a>...</a>
  <p>...</p>
</template>
// This is equivalent to not having a root node at this point, and the root tag is added internally <fragment> </fragment>
// The benefit is that you can reduce the label hierarchy and reduce memory consumption

6. key on template
1)vue2
If you need to add a v-for to the template for looping, the key can only be placed on a child node
[Front-end vue upgrade] vue2+js+elementUI upgrade to vue3+ts+elementUI plus

In vue3 you can bind the key to the template
[Front-end vue upgrade] vue2+js+elementUI upgrade to vue3+ts+elementUI plus

7, vue-lazyload currently does not support Vue3, need to wait for an update

8、store
vue2

import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex)
export default new Vuex.Store({
    state: {},
    mutations: {},
    actions: {},
    modules: {}
})

vue3

import {createStore} from 'vuex' // Import the createStore method
export default createStore({
    state: {},
    mutations: {},
    actions: {},
    modules: {}
})

Using vuex in vue3

import { createStore } from 'vuex'
export default createStore({
    state: {
        name: "superman",
        arr: [1, 2, 3]
    },
    mutations: {
        muModify(state, val) {
            console.log("commit muModify", val)
            state.name += val
        }
    },
    actions: {
        acModify(context, val) {
            console.log("dispatch acModify", val)
            context.commit("muModify", val)
        }
    },
    getters: {
        getArr(state) {
            return state.arr.map(item => item * 2)
        }
    },
    modules: {}
})
import { useStore } from "vuex";
export default {
    name: "App",
    setup() {
        // Using Vuex with useStore
        const {state,getters,dispatch, commit} = useStore();
        // Getting data
        let name = state.name;
        let arr = computed(() => state.arr);
        let getArr = computed(() => getters.getArr);
        // The computed method is used to make the data responsive.
        // computed is roughly the same type as useCallBack in react.
        
        // Call the dispatch method
        function diModify() {
            dispatch("acModify", "(Actions)");
        }
        // Call the commit method
        function coModify() {
            commit("muModify", "(Mutations)");
        }
        return { name, arr, getArr, coModify, diModify };
    },
};
</script>

Recommended Today

uniapp and applet set tabBar and show and hide tabBar

(1) Set the tabBar: uni.setTabberItem({}); wx.setTabberItem({}); indexnumberisWhich item of the tabBar, counting from the left, is indexed from 0.textstringnoButton text on tabiconPathstringnoImage PathselectedIconPathstringnoImage path when selectedpagePathstringnoPage absolute pathvisiblebooleannotab Whether to display uni.setTabBarItem({ index: 0, text: ‘text’, iconPath: ‘/path/to/iconPath’, selectedIconPath: ‘/path/to/selectedIconPath’, pagePath: ‘pages/home/home’ }) wx.setTabBarItem({ index: 0, text: ‘text’, iconPath: ‘/path/to/iconPath’, selectedIconPath: ‘/path/to/selectedIconPath’, pagePath: ‘pages/home/home’ }) […]