Vue. Js: How to Migrate a Large Project from Vue 2 to Vue 3
The vue.js team released Vue 3.0 in September 2020. This all-new release brings many new features, optimizations, and some ground-breaking changes.
In June 2021, Vue 3.1 was released by the vue.js team. This new release comes with @vue/compat(also called “Migration build”). It allows for easy migration of large projects from Vue 2 to Vue 3 by running Vue 2 and Vue 3 code.
Migrating to Vue 3 can be an important task (depending on the size of your project). At Crisp, we recently spent 2 weeks migrating our app (250,000 lines of code) from Vue 2.6 to Vue 3.2
We prepared our migration by reading the official migration manual, but we found many different issues when migrating the project.
We felt it would be good to share our experience so that other companies could benefit from our experience. This article will tell you:
- Differences between Vue 2 and Vue 3
- The problems we encountered during the migration and the corresponding solutions
- Our vue.js migration strategy
Background on Vue 3
Vue.js was founded in 2013 with the idea of creating a minimalist alternative to AngularJS 1.
At the time, Angular was a huge framework with lots of new functionality. The first version of vue.js was like a miniature framework with templates, data bindings, filters, and directives.
Then Vue 2 was released in 2016 (around the same time as Angular 2) and provides a great alternative to AngularJS. In fact, many developers who use Angular 1 decide to migrate to Vue 2 because they don’t like Angular 2: Vue 2 offers something as simple as AngularJS, but with better performance.
Vue 3 was created with performance in mind. This is an evolution, not a revolution. Most of the new features and major changes have been released for performance reasons.
The internal reaction system has been redesigned from the ground up and, as a result, IE 11 support has been removed (which is not a huge loss 😂).
Translator’s note: IE, old industry cancer 😂
Finally, this new release aims to be more modular and introduces features such as the Composition API.
Vue 3 is the biggest change
Vue global API
The Vue global API is deprecated. While @vue/compat still supports it, to fully support Vue 3, you’ll need to redesign your application to move away from the use of global apis.
This means that apis like vue. set or vue. delete cannot be used. Because Vue 3 comes with a new responsive system, using these apis becomes useless.
You can use object[key] = value instead of Vue. Set (object, key, value). Similarly, Vue. Delete (object, key) can be replaced with delete object[key].
The translator’s note:
At the bottom of vue 2, responsivity is implemented with the Object.defineProperty API, but this API cannot intercept data changes of some objects, such as arrays. So in Vue 2 we can’t update the view directly by modifying the array subscript
Vue 3 uses the Proxy API for responsiveness. The API intercepts operations on objects, arrays, and so on.
Filters
As mentioned earlier, vue.js was created as an alternative to Angular 1. This is why filters were originally supported.
The biggest problem with filters is performance: the filter function must be executed every time data is updated. For this reason, Vue 3 has removed support for filters.
This means that we can’t be used in the Vue 3 templates similar {{user. The lastName | uppercase}} template syntax.
Instead, you’ll have to use computed properties like {{uppercase(lastName)}} or methods like {{uppercase(lastName)}} instead of filters.
Vue& plug-in initialization
Starting with Vue 3, your applications and plug-ins are no longer globally instantiated. This means that you can create multiple Vue applications in the same project.
Vue 2:
import Vue from "vue";
new Vue({
router,
render: h= > h(App)
}).$mount("#app");
Copy the code
Vue 3:
import { createApp, h } from "vue";
const app = createApp({
render: () = > h(App)
});
app.use(router);
app.mount("#app");
Copy the code
v-if + v-for
Using v-if conditions in v-for lists is possible in Vue 2. This behavior has been disabled on Vue 3 for performance reasons.
Starting with Vue 3, you will have to use calculated list properties.
v-model
The V-Model API API has undergone some changes in Vue 3.
First, the property value has been renamed to modelValue
<ChildComponent v-model="pageTitle" />
Copy the code
ChildComponent needs to be rewritten like this:
props: {
modelValue: String // previously was `value: String`
},
emits: ['update:modelValue'].methods: {
changePageTitle(title) {
this.$emit('update:modelValue', title)
}
}
Copy the code
The cool thing is that you can now have multiple v-Model custom values. Such as v – model: valueA, v – model: valueB…
$emit
In Vue 2, you can use Vue instances to create a global EventBus from vm.on and vm.on and vm.off. Starting with Vue 3, this is no longer possible because vm.on and vm.on and vm.on and vM. off have been removed from the Vue instance.
We suggest using Mitt library as an alternative:
mounted() {
this.eventbus = mitt();
eventbus.on("ready", () = {
console.log("Event received");
});
eventbus.emit("ready");
}
Copy the code
The same code for Vue 2 is:
mounted() {
this.$on("ready", () = {
console.log("Event received");
});
this.$emit("ready");
}
Copy the code
Vue 3 can still emit events from components to their parents, but all events must be declared through the new option emit (which is very similar to the existing props option).
For example, if your component has an @click attribute that is fired using this.$emit(‘click’), then you need to declare the click event in your component options as follows:
props: {
name: {
type: String.default: ""}},emits: ["click"].// events have to be declared here
data() {
return {
value: ""}}Copy the code
Migration strategy
Our app is called Crisp. It is a large business messaging application, used daily by 300,000 companies around the world. The company uses Crisp to reply to customers using the team inbox of centralized chat, email, and many other channels.
Since our current application is used by many different users, it is obviously important for us not to break anything. Therefore, we also need to improve our software every day so that we can iterate over bugs and new features.
So we need to have two different branches:
- Our main branch, for Vue 2.6, has our current release life cycle
vue3
A branch for migrating Vue 2 to Vue 3
In addition, we released a daily beta running the Vue 3 code base and reverse-ported the changes we made almost every day on the Vue 2 branch to Vue 3 to prevent headaches after merging the Vue 3 code base.
Finally, we chose not to rely on new Vue apis, such as the Composition API, and migrated only the necessary code. The reason behind this is to reduce the risk of introducing regression.
Update the Vue Build tool
Our application relies on the Vue Cli (Webpack). We chose not to migrate to Vite to prevent us from introducing new problems because our build system was very complex.
Migrating the Vue Cli to support Vue 3 is very easy.
You must first edit the package.json file to update vue.js and its dependencies.
So we replace “vue”: “^2.6.12” with “vue”: “^3.2.6”.
In addition, we will have to use “@vue/compat”: “^3.2.6” to smoothly migrate the code base from Vue 2 to Vue 3.
We also update “VUe-template-compiler “: “^2.6.12” to “@vue/ compiler-sFC “: “^3.2.6”.
Now we will have to update our vue.config.js file and edit the chainWebpack function. It will force all of your existing libraries to use the @vue/compat package.
// Vue 2 > Vue 3 compatibility mode
config.resolve.alias.set("vue"."@vue/compat");
config.module
.rule("vue")
.use("vue-loader")
.loader("vue-loader")
.tap(options= > {
// Vue 2 > Vue 3 compatibility mode
return {
...options,
compilerOptions: {
compatConfig: {
// default everything to Vue 2 behavior
MODE: 2}}}; });Copy the code
Update our main.js file
We now need to instantiate Vue like this:
import { createApp, h, configureCompat } from "vue";
const app = createApp({
render: () = > h(App)
});
app.use(router);
app.use(store);
// Initiate other plugins here
configureCompat({
// default everything to Vue 2 behavior
MODE: 2
});
app.mount("#app");
Copy the code
Update the Vue Router
We will have to use the latest version of Vue Router “VUe-Router” : “4.0.11”
Using the latest vue.js Router doesn’t make much difference. The main difference is that you must manually enable the history mode using the following methods:
import { createWebHistory, createRouter } from "vue-router";
var router = createRouter({
history: createWebHistory(),
routes: [
// all your routes]});Copy the code
Migrating Filters
Our existing application relies heavily on filters (about 200 uses). The first thing is to find out how many filters we use. Since modern ides (VSCode, SublimeText) support regular expression search, we made a regular expression so that we could find all Vue filters in our template: {{(.*?) \ | (. *?) }}
Sublime Text’s regular search results
Since Vue 3 dropped filter support completely, we tried to find an elegant way to still use filters. The solution is to migrate Vue filters to custom Singletons Helpers.
For example on Vue 2:
Vue.filter("uppercase".function(string) {
return string.toUpperCase();
});
Copy the code
On Vue 3 it becomes:
uppercase(string) {
return string.toUpperCase();
}
export { uppercase };
Copy the code
We then instantiate the StringsFilter class globally:
import { uppercase } from "@/filters/strings";
app.config.globalProperties.$filters = {
uppercase: uppercase
};
Copy the code
Finally, we can use our filters
{{firstName | uppercase}} to {{$filters. Uppercase (firstName)}}
Fixed error
As the migration process progresses, you will find errors in the browser console. Vue 3 compatibility mode comes with different logs to help you migrate your applications to Vue 3.
Vue 3 compat mode comes with explicit warnings
Instead of trying to migrate your application functionality function-by-function or template-by-template, we recommend that you migrate apis by API.
For example, if you see a WATCH_ARRAY deprecation notice in the log, we recommend that you review the migration guidance provided in the log.
Then, gradually migrate and observe step by step.
After all migrations are complete, then you can turn off compatibility mode for this feature:
import { createApp, h, configureCompat } from "vue";
const app = createApp({
render: () = > h(App)
});
// Initiate all plugins here
configureCompat({
// default everything to Vue 2 behavior
MODE: 2.// opt-in to Vue 3 behavior for non-compiler features
WATCH_ARRAY: false
});
app.mount("#app");
Copy the code
Update the rely on
Vue 3 comes with the @vue/compat package and supports Vue 2 and Vue 3 libraries. However, the @vue/compat pattern also brings performance degradation.
Compatibility mode should only be used when converting your application to Vue 3 and all of its libraries. After converting all projects and libraries, you can get out of compatibility mode.
So in order for us to achieve this, we had to migrate all our libraries to Vue 3 compatible libraries.
All official Vue libraries (Vuex, Vue Router) have been ported to Vue 3, and most popular packages already have Vue 3-compatible versions.
About 20% of the community libraries we use do not have Vue 3 versions, so we chose to fork all the libraries that have not been ported to Vue 3, such as VUe-router-Prefetch.
Porting Vue 3 libraries is usually quite simple, taking anywhere from 30 minutes to half a day, depending on the complexity of the library.
Once we have completed the migration of a library to support Vue 3, we create a “pull request” for the original library so that others can benefit from our work.
On performance
Vue 3 brings a number of different performance improvements thanks to the new responsive response system. In most cases, the Javascript heap size has been reduced from 20-30% to 50% for some complex lists. For our messaging use case, we saw significant CPU improvements.
Our new Crisp app is faster and lighter to use.
conclusion
As of this writing, we are deploying the new Crisp application in production. Migrating the application took us about 2 weeks, and we were 2 developers, almost full-time.
By comparison, the migration from Angular 1 to Vue 2 took us about nine months.
Switching from Vue 2 to Vue 3 is an important but not impossible task. This new Vue release offers some great performance improvements.
In gratitude to the vue.js community, we have begun to contribute to the project as a gold sponsor. As a company, we felt we had to license open source projects to build a better world where software and the environment could talk to each other.