loading...

Be prepared to migrate your Vue app to Vue 3

chenxeed profile image Albert Mulia Shintra Updated on ・7 min read

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

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 🤯. 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, 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 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. 😉

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

// 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');
// 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, 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. 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:

<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:

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

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

// 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?

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:

// 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:

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

// your vue object
{
  methods: {
    toCurrency 
  }
}

note: just toCurrency will work ;) thanks to ES6 object property shorthand

Refactor your component model into .sync

Based on the RFC docs, Vue 3 will deprecate the model option in the Vue component, and it will replace sync 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:

// 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:

// 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 3

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

Easy peasy lemon squeezy! 😋

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:

// 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:

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. 🤗

Use @vue/composition-api to write your component

I really appreciate the Vue community for providing the @vue/composition-api 🥰. Not only it can be used for the developer to hands-on using the Composition API, 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:

// BEFORE
import Vue from 'vue';

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

Simply replace it to use defineComponent:

// 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:

// 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. ;)

What's More

There will be another breaking changes such as:

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

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 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!

Posted on by:

chenxeed profile

Albert Mulia Shintra

@chenxeed

Web Developer with the passion of solving problem and clean solution.

Discussion

markdown guide
 

Simple and useful, thx.
Where's the link to this "if you love Typescript,.... I'll write a separated article about it ;)" ?

 

Thanks for checking!

I'll post a new article in a few days, will share it here once I do!

 
 
 

Using composition API will break Vetur if you’re using the template interpolation feature (where it type checks the html and verifies bound properties exists)

In addition the Vue dev tools will crash significantly more often as it does not officially support the plugin.

So if you rely on that I’d recommend not installing the plugin.

 

Thank you for the great article! It was very useful for me 👍

I would like to ask you for permission to translate this article into Korean and publish it on a company run blog (ui.toast.com/). I will quote the source and in no way use it to gain any monetary value.

Please let me know what you think, and hope you have a great day :)

 

Hi, feel free to use this article! It's my honour 👍

 

And if you are using vue-property-decorator?

 

Ah, good concern! I missed to mention it in the article above, that the Vue Class Component is also in the discussion to be available in Vue 3. I think the issue below mentioned some breaking changes to be noted:

github.com/vuejs/vue-class-compone...

 

Wow, the example given looks really weird with watchers being external. We use vue-property-decorator which would do things like

import { Component, Ref, Watcher, Vue } from 'vue-property-decorator';
import NestedComponent from './NestedComponent';
@Component({ components: { NestedComponent }})
export default class MyClass extends Vue {
  @Ref()
  private someRef?: HTMLInputElement;
  private someData: string;
  private someComputed(): string { return window.localStorage.getItem('foobar') ?? '' }
  @Watcher('someData', { immediate: true })
  private onSomeDataChanged(newValue: string, oldValue?: string) {
    if(this.someRef) { this.someRef.focus(); }
  }
}

Just hope there are no super significant changes. We have over 925 *.vue files, all of them using vue-property-decorator

Yeah it'll be a hassle to migrate each of your vue files. Hopefully they don't deprecate the common interfaces in the new version for Vue 3.

 
 

I'll rather not do anything until Vue3 finally officially comes out. It seems to take forever.

 
 

Very useful, thanks for the post!

 

Thanks! I'll come back to this article when Vue 3 is out and it's time for us to upgrade. But it's good to know what to avoid in the meantime. 💯

 
 

Thanks! I'll rewatch my code.

 

You're welcome! Hope it helps!