Awesome breaking changes in Vue 3, if you migrate from Vue 2
Tags: javascript, vue, changelog, discuss
Published: 2020-05-01 18:38:04

Heya!

Thanks to the Vue Core team, we can try out the most anticipated Vue 3 which has a lot of great improvements and features to look forward.

Currently it's in the beta release, and if you want to get hands-on with the latest changes you can try to [install it](https://dev.to/chenxeed/install-vue3-beta-with-vuex-and-vuerouter-easily-with-vue-cli-2h94) and followup the changes in their [github release](https://github.com/vuejs/vue-next/releases).

As a VueJS developer, I have experienced the current code implementation of Vue 2, and when I tried the Vue 3, I noticed there are some breaking changes. But don't get me wrong, **I personally like the breaking changes** as I believe it will help to improve the code quality and lesser chance of unexpected bugs. Also, these breaking changes are coming from the agreed [RFC](https://github.com/vuejs/rfcs/tree/master/active-rfcs) by the Vue Core team, so check them out for more detail.

Alas, here we go!

# Vue app initialization

In the Vue 2, usually the plugin installation will be done in the global instance of the `Vue` object, and then we create a `new` instance of the Vue to initialize the Vue app. For example:

```js
// Vue 2 example of initialization
import Vue from 'vue';
import Vuex from 'vuex';
import App from './App.vue';

// install vuex plugin
Vue.use(Vuex);
const store = new Vuex.store(/* store configuration object */);

// initiate the vue app
new Vue({
  store,
  render: (h) => h(App),
}).$mount('#app');
```

This implementation has a flaw if you need to create multiple `Vue` app in the same page. Since it uses the global `Vue` instance to install the app, you can't initiate multiple `Vue` app with different plugins to be installed. This can lead to the pollution of the Vue instance.

In the Vue 3, plugin installation and app initialization are immutable from the original Vue instance, since you must initiate the Vue app first before installing the plugins.

```js
// Vue 3 example of initialization
import { createApp } from 'vue';
import { createStore } from 'vuex';
import App from './App.vue';

// initialize the store object
const store = createStore(/* store configuration object */);

// create the app and install the store
createApp(App)
  .use(store)
  .mount('#app');
```

Notice that there's no global `Vue` defined and mutated here. With this, now you can be sure that every plugin used on each application is specific and won't pollute other `Vue` app. Profit! :smile:

_More details and the reason behind in the RFC: https://github.com/vuejs/rfcs/blob/master/active-rfcs/0009-global-api-change.md_

# Allow multiple `v-model`, and deprecate `model` option

:heartbeat: Before I make anyone panic here, please note that **this changes on `v-model` API is not affecting the usage of `v-model` in the native elements** like `<input>, <select>, <textarea>`, etc.

The `model` option that I referred on the title above is [the model option](https://vuejs.org/v2/api/#model) that we use for custom `v-model` on the custom component. So yes, this breaking change is only meant for the custom component that uses `model` option, as [mentioned here](https://github.com/vuejs/rfcs/blob/master/active-rfcs/0011-v-model-api-change.md#detailed-design) that **it will be removed in the Vue 3**.

In the Vue 2, you can only define one `v-model` to have a two-way data binding. If you need multiple props to have two-way data binding, you can use [.sync](https://vuejs.org/v2/guide/components-custom-events.html#sync-Modifier) instead.

```vue
// Vue 2 example of v-model and sync
<my-custom-component
  v-bind:title.sync="title"
  v-model="visible"/>
```

Having `v-model` and `.sync` directive [takes more learning curve](https://forum.vuejs.org/t/sync-vs-v-model/19380) while they are doing similar things. Thus, in Vue 3 the `.sync` are deprecated, and then you can use multiple `v-model` instead! :heart_eyes:

```vue
Vue 3 example of v-model
<my-custom-component
  v-model:title="title"
  v-model:visible="visible"/>
```

More consistent, so less bikeshedding with your teammates, profit! :smile:

_More details and the reason behind in the RFC: https://github.com/vuejs/rfcs/blob/master/active-rfcs/0011-v-model-api-change.md_

# Event Bus is deprecated

Do you love the concept of [Event Bus in Vue](https://alligator.io/vuejs/global-event-bus/)?

If yes, then this might disappoint you a bit. Vue has no official documentation for Event Bus, but the API built in the Vue 2 instance allows us to create a publish-subscribe object with the `vm.$emit` and `vm.$on` method.

```js
// Vue 2 example of event bus

const eventBus = new Vue();

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

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

There is a good motivation behind this changes, because Vue encourages more state-driven data flow, which data are passed from parent components to its child, and events are emitted from the child to the parent component. Profit! :smile:

But of course using Event Bus pattern is still allowed in the Vue 3. If you still need it, you can install any 3rd party library or write your own.

_More details and the reason behind in the RFC: https://github.com/vuejs/rfcs/blob/master/active-rfcs/0020-events-api-change.md_

# `filter` is deprecated

[Filter](https://vuejs.org/v2/guide/filters.html) is one of the early feature introduced by Vue to easily map your rendered value. It is usually used for price tag, currency, and capitalize.

The usage of filter is usually to beautify your vue template code:

```vue
// Vue 2 example of filter
<div class="name">{{ user.name | capitalize }}</div>

<script>
import capitalize from './filter/capitalize';
export default {
  /* vue configuration */,
  filter: {
    capitalize
  }
}
</script>
```

Filter are encouraged for simplicity and re-usability. But, it is also replaceable with `methods` because there is no difference in terms of performance. Removing `filter` will encourage more explicit methods to be defined on each component, and if you need to reuse the filter function on multiple components, it can just be simply imported and registered as part of the method.

```vue
// Vue 3 example of filter
<div class="name">{{ capitalize(user.name) }}</div>

<script>
import capitalize from './filter/capitalize';
export default {
  /* vue configuration */,
  methods: {
    capitalize
  }
}
</script>
```

This is just my personal opinion, but if you're looking forward for the [Vue 3 Composition API](https://composition-api.vuejs.org/), you might notice that `filter` can also be easily replaced as well by manually return the filter function in the setup.

```js
// Vue 3 composition api setup example
import { useState, useRefs } from 'vue';
import capitalize from './filter/capitalize';

setup () {
  const data = {
    user: { name: 'Albert' }
  }

  return {
    ...useRefs(data),
    capitalize
  }
}
```

Deprecating `filter` will help us to code more consistently and no more bikeshedding on deciding whether the function shall be registered in the `filter` or `method`. Profit! :smile:

_More details and the reason behind in the RFC: https://github.com/vuejs/rfcs/blob/master/active-rfcs/0015-remove-filters.md_

# Summary :star:

With the upcoming release of Vue 3, the changes are going to a better direction for the sake of the code quality. All of these breaking changes are considered carefully in [the RFC](https://github.com/vuejs/rfcs/blob/master/active-rfcs), so feel free to check and contribute to the discussion!

Lastly, thank you for going through this article! I hope this help anyone that consider to migrate their existing Vue 2 application to the Vue 3, to be aware of the changes and be prepared!

What do you think of the breaking changes? Your comment and suggestion to help me improve this documentation and my writing skill is very much appreciated!

_note: This documentation will be updated according to any changes to be made, before it finally reach the stable release._