Intro
At CodersRank we build our front-end with Vue.js. And with the recent release of Vue.js version 3 we decided to upgrade. Here, we’ll cover what needs to be changed to migrate to the all new Vue.js based on our experience.
We have 2 main projects. One project is the large developers profile website and the other one is a smaller admin section. To start, we decided to migrate the smaller admin section to see how hard it is to migrate (spoiler: not hard).
So, let’s start.
Init Vue App
Let’s start at the main app entrypoint script. Vue.js version 3 uses new Global API and requires a different approach to initialize the app:
In Vue 2 we had:
// Import Vue import Vue from "vue"; // Import main App component import App from "./App.vue"; // Init Vue app const app = new Vue({ // element where to mount the app el: "#app", // render main component render: (h) => h(App), });
And in Vue 3 it is now:
// Import createApp function import { createApp } from 'vue'; // Import main App component import App from './App.vue'; // Init Vue app and pass main app component const app = createApp(App); // Mount app app.mount('#app');
Vuex Store
If you use Vuex state management library, then it also needs to be updated to the latest version for Vue 3. And Vuex v4 also has new global API changes.
Let’s look at how we use that and the init Vuex store in Vue 2:
import Vue from "vue"; // Import Vuex import Vuex from "vuex"; import App from "./App.vue"; // Tell Vue.js to use Vuex plugin Vue.use(Vuex); // Create store instance const store = new Vuex.Store({ state: { /* ... */ }, getters: { /* ... */ }, mutations: { /* ... */ }, actions: { /* ... */ }, }); const app = new Vue({ el: "#app", render: (h) => h(App), // pass store instance store: store, });
The same but in Vue 3 should be the following:
import { createApp } from 'vue'; // Import createStore function import { createStore } from 'vuex'; import App from './App.vue'; const app = createApp(App); // Create store instance const store = createStore({ state: { /* ... */ }, getters: { /* ... */ }, mutations: { /* ... */ }, actions: { /* ... */ }, }) // Tell app to use store app.use(store); // Mount app app.mount('#app');
Slots
In our Vue 2 app we were still using legacy slots API:
<some-component> <h1 slot="header">Title</h1> <p slot="content">Content</p> </some-component>
It is required to be changed to a new one using <template> tags:
<some-component> <template #header> <h1>Title</h1> </template> <template #content> <p>Content</p> </template> </some-component>
v-model
v-model also has new syntax in Vue 3. For example, if in Vue 2 we had the following component:
<custom-input v-model="inputValue" /> <template> <input :value="value" @input="onInput" /> </template> <script> export default { model: { // specify prop that will be modified by v-model props: 'value', // specify event that should be received by v-model with the new value event: 'input', }, props: { value: String, }, methods: { onInput(e) { this.$emit('input', e.target.value), }, }, } </script>
In Vue 3, by default, it expects that v-model should be bound to modelValue prop of the component and emit update:modelValue event in order to update model value. So we have to change the component to the following:
<script> export default { props: { // change "value" prop to "modelValue" modelValue: String, }, methods: { onInput(e) { // emit "update:modelValue" prop with new value this.$emit('update:modelValue', e.target.value), }, }, } </script>
But also Vue 3 provides more control over it and we still keep the prop named value. In this case we need to emit update:value event:
<script> export default { props: { // keep name as value value: String, }, methods: { onInput(e) { // emit "update:value" prop with new value this.$emit('update:value', e.target.value), }, }, } </script>
And to let Vue know that we need model to be bound to the value prop instead of the default modelValue, we should use v-model like this:
<custom-input v-model:value="inputValue" />
The best thing about it is that now components can have multiple v-models:
<some-component v-model:title="titleValue" v-model:content="contentValue" />
Composition API
Vue 3 comes with a new Composition API.
It is not necessary to change all your components to the new Composition API as Vue 3 still works perfectly with the current Options API. That is why we decided to keep it at the moment.
Custom Elements (Web Components)
At CodersRank we have a nice set of web components for developers to integrate on their personal websites. We also use them on our website:
It was not so straightforward to make Vue 3 understand them properly and not to think these are not Vue components.
In Vue 2 to specify custom elements, we used Vue.config.ignoredElements:
import Vue from "vue"; Vue.config.ignoredElements = ["codersrank-activity", "codersrank-skills-chart"];
In Vue 3 it is decided whether it is a custom element or not during template compilation phase, so it should be specified in webpack config Vue loader options:
{ test: /\.vue$/, use: { loader: 'vue-loader', options: { compilerOptions: { // ignore elements that starts with codersrank- isCustomElement: (tag) => tag.indexOf('codersrank-') === 0, }, }, }, },
TypeScript
Vue 3 has much better TypeScript support. And during the migration to Vue 3 all we needed to change was the component declaration in single-file components:
In Vue 2 we used Vue.extend to define the Vue component:
<template> <!-- ... --> </template> <script> import Vue from "vue"; export default Vue.extend({ props: { // ... }, data() { // ... }, // ... }); </script>
And in Vue 3 we need to use the new defineComponent function:
<template> <!-- ... --> </template> <script> import { defineComponent } from "vue"; export default defineComponent({ props: { // ... }, data() { // ... }, // ... }); </script>
Post Scriptum
In this article we have covered just the basics that we faced ourselves in our own project during migration from Vue.js 2 to Vue.js 3. Of course there are many things to pay attention to if you use other APIs, Vue features and plugins. Worth to mention:
- Official Vue.js 3 migration guide
- Official Vue Router migration guide for Vue.js 3 (in case you use Vue Router)
- New built-in Teleport functionality (if you used portal-vue plugin which is not yet supported for Vue.js 3)
- Composition API Reference
- Render function API changes
About CodersRank
Interested in learning more about how we help developers? Check out the details here & create your own profile!