DEV Community

Peter Karakas
Peter Karakas

Posted on • Updated on • Originally published at blog.codersrank.io

Vue 3 Basic Tutorial (Migration plan for Vue 2 users)

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),
});
Enter fullscreen mode Exit fullscreen mode

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');
Enter fullscreen mode Exit fullscreen mode

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,
});

Enter fullscreen mode Exit fullscreen mode

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');
Enter fullscreen mode Exit fullscreen mode

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>
Enter fullscreen mode Exit fullscreen mode

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>
Enter fullscreen mode Exit fullscreen mode

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>

Enter fullscreen mode Exit fullscreen mode

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>

Enter fullscreen mode Exit fullscreen mode

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>

Enter fullscreen mode Exit fullscreen mode

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" />
Enter fullscreen mode Exit fullscreen mode

The best thing about it is that now components can have multiple v-models:

<some-component v-model:title="titleValue" v-model:content="contentValue" />
Enter fullscreen mode Exit fullscreen mode

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"];

Enter fullscreen mode Exit fullscreen mode
import Vue from "vue";
Vue.config.ignoredElements = ["codersrank-activity", "codersrank-skills-chart"];
Enter fullscreen mode Exit fullscreen mode

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,
      },
    },
  },
},

Enter fullscreen mode Exit fullscreen mode

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>

Enter fullscreen mode Exit fullscreen mode

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>

Enter fullscreen mode Exit fullscreen mode

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:

About CodersRank

Interested in learning more about how we help developers? Check out our website here & create your own profile!

Latest comments (0)