Upgrade/Migrate your Vue 2.x projects to Vue 3.0

Jonathan Ma
4 min readOct 28, 2020

With the release of vue-next v3.0.0-beta.1 in April 2020, as of today, Vue has reached an official v3.0 release — One Piece. As a fellow Vue junkie, I am feeling the same excitement as any other frontend devs because of the new features and enhancements this would bring. However, that brings the question: “how to upgrade my existing Vue 2 projects to Vue 3?”.

Vue 3.0 “One Piece”

Taking a look at the release notes, we see that Vue 3 brings changes to the core library as well as the Vue ecosystem:

We’ll be looking at how to scaffold Vue 3 projects based on existing Vue 2 code, and show deployable code than sandboxed tutorials out there. A brief outline here:

  • Setting up
  • Writing Components
  • Integrate vue-router (v4.x)
  • Integrate vuex (v4.x)

Setup Syntax

In Vue 2.x you would set up with the following:

import Vue from 'vue'
import App from './App.vue'
new Vue({
render: (h) => h(App)
}).$mount(“#app”);

We are using the global Vue object to create a Vue instance. Any change made to this Vue object will affect every Vue instance and component.

In Vue 3, a new function createApp is provided so now every configuration is scoped to a certain Vue application defined:

import { createApp } from 'vue'
import App from './App.vue'
const app = createApp(App)
app.mount('#app')

Back in Vue 2.x, if a third party library is modifying the Vue object, it creates unexpected changes (especially with global mixins) which would not be the case with Vue 3.

Additionally, you can use these methods in app: config, use, mixin, component, directive, mount, unmount, provide/inject. For more info, refer to the official docs: Application API.

Writing Components

In Vue 3, the component construction hasn’t changed much. Just that computed and watch are extracted out as functions. See below:

<template>
<div class="test">
<h1>test count: {{count}}</h1>
<div>count * 2 = {{doubleCount}}</div>
<button @click="add">add</button>
</div>
</template>
<script>
import { ref, computed, watch } from 'vue'
export default {
setup () {
const count = ref(0)
const add = () => {
count.value++
}
watch(() => count.value, val => {
console.log(`count is ${val}`)
})
const doubleCount = computed(() => count.value * 2)
return {
count,
doubleCount,
add
}
}
}
</script>

Both methods can now be imported and used as needed. Rather than having a function wrapper in Vue 2.x.

Integrate Vue Router (v4.x)

Edit the filesrc/router/index.js like below:

import { createRouter, createWebHashHistory } from 'vue-router'
import Home from '../views/Home.vue'
const routes = [
{
path: '/',
name: 'Home',
component: Home
}
]
const router = createRouter({
history: createWebHashHistory(),
routes
})
export default router

In Vue 3, initializing the router is not much different to Vue 2.x. Only that the function construction is taken over by the new createRouter .

To get the current route within a component, you can do the following:

import { getCurrentInstance } from 'vue'export default {
setup () {
const { ctx } = getCurrentInstance()
console.log(ctx.$router.currentRoute.value)
}
}

Integrate Vuex (v4.x)

Edit the filesrc/store/index.js like below:

import Vuex from 'vuex'export default Vuex.createStore({
state: {
counter: {
val: 1
}
},
mutations: {
setCounterVal(state, value) {
state.counter.val = value
}
},
actions: {
},
modules: {
}
})

The syntax hasn’t changed much here. We added counter.val to the state and added a mutation to mutate counter.valsetCounterVal.

In the component, we can reference the vuex state using computed, and ctx.$store to access the mutation methods.

<template>
<div class="test">
<h1>test count: {{count}}</h1>
<div>count * 2 = {{doubleCount}}</div>
<div>state from vuex {{a}}</div>
<button @click="add">add</button>
</div>
</template>
<script>
import { ref, computed, watch, getCurrentInstance } from 'vue'
export default {
setup () {
const count = ref(0)
const add = () => {
count.value++
}
watch(() => count.value, val => {
console.log(`count is ${val}`)
})
const doubleCount = computed(() => count.value * 2)
const { ctx } = getCurrentInstance()
console.log(ctx.$router.currentRoute.value)
const a = computed(() => ctx.$store.state.counter.val)
const update = () => {
ctx.$store.commit('setCountVal', count)
}

return {
count,
doubleCount,
add,
a,
update
}
}
}
</script>

Like Vue 2.x, we would still use $store.commit to call the vuex mutation setCountVal.

Remember to return all the relevant functions or variables at the end of the component.

Summary

Apart from createApp, Vue Router and Vuex upgrades, we are also welcomed to the new Composition API. We can see Vue is going towards better APIs and simpler development workflow. The core Vue team has adopted many ideas from third party libraries and integrated them to the official framework.

We’ve only touched on the major API changes and improvements. To view the full list, do check out the Vue RFCs repo.

--

--