DEV Community

Cover image for How to migrate from Vue 2 to Vue 3?
Mad Devs for Mad Devs

Posted on • Updated on

How to migrate from Vue 2 to Vue 3?

Introduction

As we know, the official release of Vue 3 was on September 18, 2020, it’s been almost a year, and I think it’s time to move on to it. Buuuut… it’s not that simple.

At the moment, not all plugins are adapted to Vue 3, so you have to look for replacements. I tried it and you know, it didn’t work.

But to our great luck, there is a plugin that has the same API as Vue 3. It was kindly provided by the developers themselves to soften the transition from version 2 to version 3. The plugin is called @vue/composition-api. Today we will talk about it.

What to expect from this article

Important: This article is an introductory one with which I plan to start a series of articles on VUE 3. So, today I will show you just a few simple examples that will help whet your appetite and smoothly start your transition to the new version of Vue.

Let’s get started!

image

Installing @vue/composition-api

First, we need to update the packages. To do this, open a terminal and use the command:

vue upgrade
Enter fullscreen mode Exit fullscreen mode

But note that you must have Vue CLI and Node.js installed.

After running the command, you should see a list of packages that need to be updated. If the packages are updated, the message below will appear

DONE  Seems all plugins are up to date. Good work!
Enter fullscreen mode Exit fullscreen mode

Install the Vue Composition api plugin

yarn add @vue/composition-api// ornpm i @vue/composition-api
Enter fullscreen mode Exit fullscreen mode

After that, create a file in the scr folder where @vue/composition-api will be initialized. Let’s call it installCompositionApi.js and add the code

import Vue from 'vue'
import VueCompositionApi from 
'@vue/composition-api'Vue.use(VueCompositionApi)
Enter fullscreen mode Exit fullscreen mode

This file must be imported into the main.js file, and the import function must be placed first in order for the scripts to initialize properly.

1 // INIT COMPOSITION API(VUE 3) 
2 import '@/installCompositionApi.js' 
3 
4 import Vue from 'vue' 
5 import router from '@/router' 
6 ... 
7 ...
Enter fullscreen mode Exit fullscreen mode

This concludes the installation of @vue/composition-api. The library is available globally and can be used in any of the files.

Rewriting the Vuex store

The next step is to start rewriting the existing code. I would start with the Vuex store. Let’s do that. Let’s take one of the files, for example, the module responsible for the list of articles.

import { getArticles } from '@/api/articles'

export default {
  namespaced: true,

  state: () => ({
    articles: [],
  }),

  mutations: {
    SET_ARTICLES(state, articles) {
      state.articles = articles
    },
  },

  actions: {
    async getAllArticles({ commit }) {
      try {

        const articles = await getArticles()
        commit('SET_ARTICLES', articles)
      } catch (error) {
        commit('SET_ARTICLES', [])
      } 
    }, 
  },
 }
Enter fullscreen mode Exit fullscreen mode

You have to agree it’s pretty wordy. We have to write an additional layer in the form of mutations, actions to write data asynchronously. But come on.

We delete everything without remorse and add this code:

// @/modules/articles
import { ref } from '@vue/composition-api'
import { getArticles } from '@/api/articles'

const articles = ref([])

const useArticles = () => {
  const getAllArticles = async () => {
    articles.value = await getArticles()
  } 
  return { articles, getAllArticles }
}

export default useArticles
Enter fullscreen mode Exit fullscreen mode

The number of lines is reduced, and that is already good. Let’s sort it out.

The first line imports the method ref. It adds reactivity for any variable.

ref takes an argument and returns it wrapped in an object with the property value, which can then be used to access or change the value of the reactive variable.

In the code above, ref has been added for the articles variable, it is now reactive and has additional properties.

Then we see a function that gets all articles by API and writes them into the articles variable.

But please note that it writes them to the reactive value property. If it doesn’t, the value won’t change.

That’s all. Our updated store works in exactly the same way and is even much simpler.

The only question that remains is whether Vuex is needed now?

image

Rewriting the component

After updating the code in our store, let’s rewrite the component that is responsible for displaying the articles.

The component so far looks like this:

// @/pages/articles<template>
  <div class="articles">
     <h1 class="h1">
       {{ title }}
    </h1>    // Search articles
    <input v-model="searchQuery" placeholder="Поиск" />    <template>
      <ArticlesTable :articles="articles" />
    </template>
   </div>
</template>

<script> 
import { mapState, mapActions } from 'vuex'
import ArticlesTable from '@/components/Articles/Table'

export default {
   name: 'Articles',
   components: { ArticlesTable },
   props: {
     title: {
       type: String,
       default: '',
    }
  },
  computed: {
    ...mapState('articles', {
      articles: 'articles',
    }),
  },
  async created() {
    await this.getAllArticles()
  },
  watch:
    searchQuery(query => {
      this.getAllArticles()
    },
  },
  mounted() {
    document.title = this.title

    console.log(this.$route)
  },
  methods: {
    ...mapActions('articles', ['getAllArticles']),
  },
}
</script>
Enter fullscreen mode Exit fullscreen mode

Here we see the good old Vue 2 syntax. Let’s rewrite it to the new one, and we should get it like this:

// @/pages/articles<template>
   <div class="articles">
     <h1 class="h1">
       {{ title }}
     </h1>    // Search articles
     <input v-model="searchQuery" placeholder="Поиск" />    <template>
       <ArticlesTable :articles="articlesList" />
     </template>
   </div>
 </template> 

<script> 
import { 
  ref, 
  computed, 
  watch, 
  onMounted 
} from '@vue/composition-api' 
import useArticles from '@/modules/articles' 
import ArticlesTable from '@/components/Articles/Table'

export default { 
  name: 'Articles',  components: { ArticlesTable },
  props: { 
    title: { 
      type: String, 
      default: '', 
    } 
  }
  setup(props, context) {
    const searchQuery = ref('') 
    const { articles, getAllArticles } = useArticles()
    const articlesList = computed(() => articles)

    onMounted(() => { 
      document.title = props.title 
       console.log(context.root.$route) 
    })
    watch(searchQuery, query => { 
      this.getAllArticles() 
    })
    getAllArticles()
    return { 
      searchQuery, 
      articlesList, 
    } 
  }, 
} 
</script>
Enter fullscreen mode Exit fullscreen mode

Let me tell you right away: in @vue/composition-api and therefore in Vue 3, “this” is no longer available. You must use context instead. I’ll talk about it a bit later.

Let’s take apart the new rewritten component.

At the beginning of the

attrs: () 
emit: () 
isServer: () 
listeners: () 
parent: () 
refs: () 
root: () 
slots: {} 
ssrContext: ()
Enter fullscreen mode Exit fullscreen mode

Object this

The global “this” object is now unavailable in components and we cannot use it to access, for example, the $route object or any other global object. To solve this, we added the property root. Now it’s the only way to access all global objects, installed plugins, etc.

Property value

Note, in the rewritten component, in the computed hook, we return the articles variable with the value property. This must be done, otherwise, the articlesList variable will have a reactive object, something like this:

{_isRef: true} 
value: (...) 
_isRef: true 
get value: ƒ value() 
set value: ƒ value(newVal) 
__proto__: Object
Enter fullscreen mode Exit fullscreen mode

In Vue 3, all reactive variables now have a value property.

But when using reactive variables in a template, you don’t need to call the value property as here:

// Wrong 
<h1>{{ title.value }}</h1>
Enter fullscreen mode Exit fullscreen mode

The object returned by the setup() function will be processed, and the value properties will be discarded.

// Right 
<h1>{{ title }}</h1>
Enter fullscreen mode Exit fullscreen mode

And in order to have access to the properties from setup in the template block, you have to return them with the return method.

At this point, I would like to complete the discussion. A more detailed discussion of the topic will follow in the future articles in our blog.

Conclusion

The process of upgrading to version 3 using the @vue/composition-api plugin was very easy because it is fully compatible with Vue 2. The syntax is uncomplicated and easy to understand. I hope you won’t have any trouble mastering it.

At this point I want to end and continue a more detailed discussion of the topics in future articles.

To learn more about the Vue Composition API, follow this link.

Thank you and see you soon!

image

Previously published at maddevs.io/blog.

Discussion (0)