Be prepared to migrate your Vue app to Vue 3
Tags: javascript, vue, tutorial, changelog
Published: 2020-07-12 16:04:48

Heya!

In this post, I would like to share my recent experience of trying out Vue 3 beta version, specifically regarding some caveats you might want to note if you are planning to migrate your existing Vue 2 application to the upgraded version, Vue 3!

The list below are going to help you to move towards Vue 3 best practices, and also to avoid some use-case in Vue 2 that you might going to have trouble with, if you're migrating to Vue 3.

Let's get it on!

![lets get it on](https://pics.me.me/lets-get-it-on-memegenerator-net-lets-get-it-on-53147989.png)

# Avoid using Vue Event Bus

***Short summary: Don't use `$on` / `$once` / `$off` API since it'll be deprecated in Vue 3.***

If you have heard of Event Bus, it's a common term used in the Vue development whenever you're facing a situation where you need to make a shortcut to emit event from a child to parent, or vice versa. You can simply search "vue event bus" in your browser, and you'll find a lot of article explaining it.

One thing to note is that, **it's not the official method recommended by Vue** :exploding_head:. Why I'm saying this, it's because you'll likely never see Event Bus mentioned in the official Vue docs. The closest reference is from [the migration guide from Vue 1.x](https://vuejs.org/v2/guide/migration.html#dispatch-and-broadcast-replaced), in which it's called "eventHub" there, and it recommends you to use Vuex instead.

> *This pattern can serve as a replacement for $dispatch and $broadcast in simple scenarios, but for more complex cases, it’s recommended to use a dedicated state management layer such as Vuex.*

You can check the [RFC docs](https://github.com/vuejs/rfcs/blob/master/active-rfcs/0020-events-api-change.md) as well to see why they don't recommend it.

Since Event Bus concept is practically a Publish-Subscribe pattern which is a common method in programming, you can actually still use the concept, but with different libraries like [mitt](https://github.com/developit/mitt). :wink:

This is the example of Event Bus, and how to refactor it:

```js
// Vue 2 example of event bus
import Vue from 'vue';
const eventBus = new Vue();

// subscribe
eventBus.$on('sandwich-made', () => console.log('sandwich made!'));

// publish
eventBus.$emit('sandwich-made');
```

```js
// Refactor to use 3rd party library like mitt
import mitt from 'mitt';
const eventBus = mitt();

// subscribe
eventBus.on('sandwich-made', () => console.log('sandwich made!'));

// publish
eventBus.emit('sandwich-made');
```

# Refactor your Filter functions to Method

Based on the [RFC docs](https://github.com/vuejs/rfcs/blob/master/active-rfcs/0015-remove-filters.md), filter are going to be removed.

Filter was there to help you "format" your value, for ex: to uppercase, add currency, short url, etc. Perhaps it's also inspired by [Angular Filter](https://docs.angularjs.org/api/ng/filter/filter). It looks nice since you can implement it in your template syntax. For example, here's a filter `toCurrency` to add currency format to the price integer value:

```vue
<div class="currency">{{ price | toCurrency }}</div>
```
*note: price value is 25, then filtered by `toCurrency` to $25.00*

Although it looks nice, keep in mind that it's practically a "syntax sugar" since in the runtime, it will always run `toCurrency` to format the price whenever it's updated.

If you refactor `toCurrency` as a `method` instead, it'll be written like this:

```vue
<div class="currency">{{ toCurrency(price) }}</div>
```

The refactor in your Vue script is simply to move the function from `filter` to `method`:

```js
// before
{
  filter: {
    toCurrency (value) {
      return `$${value.toFixed(2)}`
    }
  }
}

// after
{
  methods: {
    toCurrency (value) {
      return `$${value.toFixed(2)}`
    }
  }
}
```

Cool! But, what if the `filter` is registered as a global filter?

```js
Vue.filter('toCurrency', function (value) {
  return `$${value.toFixed(2)}`
})
```

In this case, I will recommend you to delete the global filter code above, and move your filter function to become a pure helper function first that can be shared. For example:

```js
// helper/filter.js

export function toCurrency (value) {
  return `$${value.toFixed(2)}`
}
```

And then, you can import the helper function to whenever component that needs to use it. For example:

```js
// price-component.vue
import { toCurrency } from './helper/filter'

// your vue object
{
  methods: {
    toCurrency 
  }
}
```
*note: just `toCurrency` will work ;) thanks to [ES6 object property shorthand](https://alligator.io/js/object-property-shorthand-es6/)*

# Refactor your component `model` into `.sync`

Based on the [RFC docs](https://github.com/vuejs/rfcs/blob/master/active-rfcs/0011-v-model-api-change.md), Vue 3 will deprecate the `model` option in the Vue component, and it will replace [`sync`](https://vuejs.org/v2/guide/components-custom-events.html#sync-Modifier) to become multiple `model`.

If you have used `model` option in your component to set two-way data binding, you can refactor it to become `.sync` instead. Here's the example:

```vue
// BEFORE

// parent component
<child-component v-model="visible"/>

// the model option in the child component's Vue object
<script>
{
  model: {
    prop: 'visible',
    event: 'change'
  }
}
</script>
```

Refactor it to use `.sync`:

```vue
// AFTER

// parent component
<child-component v-bind:visible.sync="visible"/>

// delete the model option in the child component's Vue object
```

When the time comes for you to upgrade to Vue 3, you can simply rename the `.sync` to `v-model` instead:

```vue
// Vue 3

// parent component
<child-component v-model:visible="visible"/>
```

Easy peasy lemon squeezy! :yum:

# Be wary of using 3rd party plugins

The beauty of Vue framework like other framework is, it provides API for community to create their own plugin.

But in Vue 3, **there will be a breaking changes that will make some plugins not compatible anymore**. One major change is, the Plugin installation and app initialization will be immutable from the original Vue instance. For example:

```js
// BEFORE, in Vue 2
Vue.use(myPlugin);
new Vue({/* your vue initialization */});

// AFTER, in Vue 3
const app = createApp(); // new method to initialize Vue
app.use(myPlugin); 
```

It's most likely that you will not able to refactor your code to use the plugins in the Vue 3, until the author of the plugin upgrades it. So this means, you should be considerate of using the 3rd party plugins if you're planning to migrate, as this will be the blocker.

Check the issue or roadmap of the plugins you're using, to see if they're planning to upgrade to support Vue 3. This is the example of the plugins that'll support Vue 3:

- [Bootstrap Vue](https://github.com/bootstrap-vue/bootstrap-vue/issues/5196)
- [Vue Multiselect](https://github.com/shentao/vue-multiselect/issues/1291)
- [Vuetify](https://vuetifyjs.com/en/introduction/roadmap/)

If your used plugins doesn't have the plan yet to upgrade to Vue 3, you can help to contribute by asking the author in the issue to support Vue 3, or even take part to help them upgrade. :hugs:

# Use `@vue/composition-api` to write your component

I really appreciate the Vue community for providing the `@vue/composition-api` :smiling_face_with_three_hearts:. Not only it can be used for the developer to hands-on using the [Composition API](https://composition-api.vuejs.org/), but it also provide API that'll become the core method in Vue 3.

Take for example, `defineComponent`. It will become the new standard of writing a Vue component in Vue 3, and you can already use it in your Vue 2 app!

To use it, install `@vue/composition-api` and replace your Vue object component initiation. For example:

```js
// BEFORE
import Vue from 'vue';

export default Vue.extend({
  name: 'my-component',
  /* your component props, data, methods, etc. */
});
```

Simply replace it to use `defineComponent`:

```js
// AFTER
import { defineComponent } from '@vue/composition-api';

export default defineComponent({
  name: 'my-component',
  /* your component props, data, methods, etc. */
});
```

What's the difference, you ask? Practically no difference, and that's the beauty of it! Your component shall work the same as usual, and added with "bonus" that now you can already refactor your component to use Composition API if you want to:

```js
// Refactor to use setup()
import { defineComponent } from '@vue/composition-api';

export default defineComponent({
  name: 'my-component',
  setup (props) {
    /* your component props, data, methods, etc. */
  }
});
```

*note: if you love Typescript, I'm pretty sure you will love the composition API because it'll help to [improve your component typing](https://dev.to/chenxeed/consider-vue-composition-api-to-improve-the-code-quality-31ne). ;)*

# What's More

There will be another breaking changes such as:

- [Render function API change](https://github.com/vuejs/rfcs/blob/master/active-rfcs/0008-render-function-api-change.md)
- [Unify scoped slots to just slots](https://github.com/vuejs/rfcs/blob/master/active-rfcs/0006-slots-unification.md)
- [keyCode modifier to be removed](https://github.com/vuejs/rfcs/blob/master/active-rfcs/0014-drop-keycode-support.md)
- [inline-template to be removed](https://github.com/vuejs/rfcs/blob/master/active-rfcs/0016-remove-inline-templates.md)

But if you're not using it much, and you believe it can be refactored easily, then it's up to you to decide if it shall be changed soon or later.

# Summary

![phew](https://image.shutterstock.com/image-vector/illustration-phew-sound-kid-boy-260nw-1438891808.jpg)

Phew! I hope this article helps you to be prepared when the time comes to upgrade to the Vue 3. As a Vue developer, I know that I am excited to see it coming, since it uses better API to handle reactivity, more Typescript support, and [better practices](https://dev.to/chenxeed/awesome-breaking-changes-in-vue-3-if-you-migrate-from-vue-2-3b98) in the development.

If I missed any API or notes to be defined, please let me know and I appreciate your feedback. Thank you and have a great day!