A taste of Vue.js 3: API Changes, Async Components, and Plugins
Tags: vue, vue3, javascript
Published: 2020-05-30 19:10:19

---
title: A taste of Vue.js 3: API Changes, Async Components, and Plugins
published: true
date: 2020-05-29 23:26:00 UTC
tags: vuejs,vue-3,javascript
canonical_url: https://nystudio107.com/blog/a-taste-of-vue-js-3-async-components-and-plugins
---

# A taste of Vue.js 3: API Changes, Async Components, and Plugins

### This arti­cle takes you through changes that have to be made when mov­ing to Vue.js 3, cov­er­ing API changes, async com­po­nents, and adapt­ing exist­ing plugins

Andrew Welch / [nystudio107](https://nystudio107.com)

![Vue js 3](https://nystudio107-ems2qegf7x6qiqq.netdna-ssl.com/img/blog/_1200x675_crop_center-center_82_line/vue-js-3.jpg)

We’re cur­rent­ly in the plan­ning phase for a project, and are choos­ing the tech­nolo­gies that we’ll be using as the basis for it.

[Vue.js](https://vuejs.org/) will be amongst those tech­nolo­gies, but should we go with Vue 2 or Vue 3, which is cur­rent­ly still a beta?

<aside>
                                It’s at that awk­ward stage where it could go either way
                            </aside>

At the time of this writ­ing, Vue.js 3 is at ver­sion <kbd>3.0.0-beta 14</kbd>, and is slat­ed for release Q2 2020. For now, it can be found at the [vue­js/vue-next](https://github.com/vuejs/vue-next) GitHub repo.

What we decid­ed to do was attempt to con­vert over the scaf­fold­ing we use in the [nystudio107/​craft](http://github.com/nystudio107/craft) repo and detailed in the [An Anno­tat­ed web­pack 4 Con­fig for Fron­tend Web Devel­op­ment](https://dev.to/gaijinity/an-annotated-webpack-4-config-for-frontend-web-development-1p36) article.

If every­thing went smooth­ly, then away we go… Vue.js 3 it is. If not, then we stick with Vue.js 2.

We were pri­mar­i­ly inter­est­ed in using the new [Com­po­si­tion API](https://composition-api.vuejs.org/), bet­ter [Type­Script sup­port](https://dev.to/lmillucci/building-a-vue-3-component-with-typescript-4pge), and some oth­er key improve­ments in Vue.js 3.

<aside>
                                But most­ly, the ver­sion of Vue we picked would be in use for some time
                            </aside>

This arti­cle dis­cuss­es the changes we need­ed to make to con­vert the scaf­fold­ing over. It shows some real-world sit­u­a­tions and anno­tates the changes we had to make to get the code up and run­ning on Vue.js 3.

This arti­cle does­n’t detail every change in Vue.js 3, for that check out the [Vue 3 Tuto­r­i­al (for Vue 2 users)](https://vuejsdevelopers.com/2020/03/16/vue-js-tutorial/) arti­cle and the [New awe­some­ness com­ing in Vue.js 3.0](https://devmode.fm/episodes/new-awesomeness-coming-in-vuejs-3-0) podcast.

**Spoil­er alert:** it went well, we’re using Vue.js 3 for this project!

## Overview of the Changes

The changes we’re going to be mak­ing here are real­ly rel­a­tive­ly triv­ial. We have a sin­gle JavaScript entry point <kbd>app.js</kbd> file, and a [Vue­Con­fet­ti](https://github.com/alexandermendes/vue-confetti) component.

This skele­ton code is what I use for my scaf­fold­ing, because it’s nice to see some con­fet­ti to indi­cate that your code is work­ing as intended.

The <kbd>app.js</kbd> is just a shell that does­n’t do much of any­thing oth­er than load the Vue­Con­fet­ti com­po­nent, but the project does demon­strate some inter­est­ing things:

- Changes need­ed to your <kbd>package.json</kbd> file
- Changes need­ed to your web­pack config  
- The changes need­ed to instan­ti­ate a new Vue app
- How to do [web­pack dynam­ic imports](https://webpack.js.org/guides/code-splitting/#dynamic-imports) of Vue 3 APIs
- How to use async com­po­nents in Vue 3 using new [Async Com­po­nent API](https://github.com/vuejs/rfcs/pull/148)
- How we can adapt a Vue plu­g­in that assumes being able to glob­al­ly inject instance prop­er­ties via <kbd>Vue.prototype</kbd>

If you’re using [vue-cli](https://cli.vuejs.org/), there’s a [vue-cli-plu­g­in-vue-next](https://github.com/vuejs/vue-cli-plugin-vue-next) plu­g­in that will auto­mate some of the project con­ver­sion for you, but I want­ed to get my hands dirty.

If you’re inter­est­ed in see­ing all of the major changes in Vue.js 3, check out the [Vue.js merged RFCs](https://github.com/vuejs/rfcs/pulls?q=is%3Apr+is%3Amerged+label%3A3.x).

And now, with­out fur­ther ado… let’s get on with the show!

## Package.json Changes

The first thing we need to do is con­vert over the <kbd>package.json</kbd> pack­ages to ver­sions that work with Vue.js 3.

Here are just the additions/​changes need­ed (not the com­plete <kbd>package.json</kbd>):

```

{
    "devDependencies": {
        "@vue/compiler-sfc": "^3.0.0-beta.2",
        "css-loader": "^3.4.2",
        "file-loader": "^6.0.0",
        "mini-css-extract-plugin": "^0.9.0",
        "vue-loader": "^16.0.0-alpha.3"
    },
    "dependencies": {
        "vue": "^3.0.0-beta.14"
    }
}

```

## web­pack con­fig changes

Next up we need to make some very minor changes to the web­pack con­fig detailed in the [An Anno­tat­ed web­pack 4 Con­fig for Fron­tend Web Devel­op­ment](https://dev.to/gaijinity/an-annotated-webpack-4-config-for-frontend-web-development-1p36) article.

We just need to make two changes in the <kbd>webpack.common.js</kbd> file, and we’re done.

First, we need to change how we import the [Vue­Load­er­Plu­g­in](https://vue-loader.vuejs.org/guide/#vue-cli):

```

const VueLoaderPlugin = require('vue-loader/lib/plugin');

```

To look like this:

```

const { VueLoaderPlugin } = require('vue-loader');

```

Next up we need to change what file we alias <kbd>vue</kbd> to, by changing:

```

        alias: {
            'vue$': 'vue/dist/vue.esm.js'
        },

```

To look like this:

```

        alias: {
            'vue$': 'vue/dist/vue.esm-bundler.js'
        },

```

## app.js Changes

House­keep­ing out of the way, now we can get into the actu­al changes in the JavaScript & Vue components.

Here’s what the skele­ton <kbd>app.js</kbd> looked like for Vue.js 2:

```

// Import our CSS
import styles from '../css/app.pcss';

// App main
const main = async () => {
    // Async load the vue module
    const { default: Vue } = await import(/* webpackChunkName: "vue" */ 'vue');
    // Create our vue instance
    return new Vue({
        el: "#page-container",
        components: {
            'confetti': () => import(/* webpackChunkName: "confetti" */ '../vue/Confetti.vue'),
        },
        data: {
        },
    });
};

// Execute async function
main().then( (vm) => {
});

// Accept HMR as per: https://webpack.js.org/api/hot-module-replacement#accept
if (module.hot) {
    module.hot.accept();
}

```

We have an [async func­tion](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/async_function) <kbd>main()</kbd> that [awaits](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/await) the [promise](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise) returned by the [web­pack dynam­ic import](https://webpack.js.org/guides/code-splitting/#dynamic-imports) of the [Vue con­struc­tor](https://012.vuejs.org/api/).

This pat­tern allows the main thread to con­tin­ue exe­cut­ing while web­pack han­dles dynam­i­cal­ly load­ing the <kbd>vue</kbd> chunk.

While this is some­what point­less in the skele­ton code, this type of dynam­ic import­ing allows for code split­ting that becomes ben­e­fi­cial from a per­for­mance point of view as our appli­ca­tion gets fleshed out.

Then we cre­ate a new View­Mod­el, adding in our [async com­po­nent](https://vuejs.org/v2/guide/components-dynamic-async.html#Async-Components) <kbd>Confetti.vue</kbd> (we’ll get to the com­po­nent in a bit).

Let’s have a look at the changes we need to make to this code to get it work­ing on Vue.js 3:

```

// Import our CSS
import styles from '../css/app.pcss';

// App main
const main = async () => {
    // Async load the Vue 3 APIs we need from the Vue ESM
    const { createApp, defineAsyncComponent } = await import(/* webpackChunkName: "vue" */ 'vue');
    // Create our root vue instance
    return createApp({
        components: {
            'confetti': defineAsyncComponent(() => import(/* webpackChunkName: "confetti" */ '../vue/Confetti.vue')),
        },
        data: () => ({
        }),
    }).mount("#page-container");
};

// Execute async function
main().then( (root) => {
});

// Accept HMR as per: https://webpack.js.org/api/hot-module-replacement#accept
if (module.hot) {
    module.hot.accept();
}

```

The glob­al <kbd>Vue</kbd> con­struc­tor is gone in Vue.js 3, and instead we need to explic­it­ly import the func­tions from the Vue.js 3 API that we need.

In this case, we’re going to need <kbd>createApp()</kbd> to cre­ate our app instance, and we’ll need <kbd>defineAsyncComponent()</kbd> to uti­lize the new [Async Com­po­nent API](https://github.com/vuejs/rfcs/pull/148) for using async components.

<kbd>createApp()</kbd> returns an app instance, which has an app con­text that is avail­able to all com­po­nents in the com­po­nent tree.

Unlike Vue.js 2, this app does­n’t auto­mat­i­cal­ly mount, so we call <kbd>.mount("#page-container")</kbd>, which returns the root com­po­nent instance, which mounts on the DOM ele­ment with the id <kbd>page-container</kbd>.

To get our async com­po­nent <kbd>Confetti.vue</kbd> work­ing, all we need to do is wrap the func­tion we used in Vue.js 2 with <kbd>defineAsyncComponent()</kbd>.

Also of note is that data can no longer be an object, but rather needs to be a fac­to­ry func­tion that returns a data object. While you’d often do this in Vue.js 2 already, it’s now manda­to­ry in Vue.js 3.

If you want to learn more about some of these glob­al API changes, check out the [Glob­al API Change RFC](https://github.com/vuejs/rfcs/blob/master/active-rfcs/0009-global-api-change.md).

## Confetti.vue changes

Now onto the all impor­tant <kbd>Confetti.vue</kbd> component! 🎉

The exist­ing code for the <kbd>Confetti.vue</kbd> com­po­nent looks like this, and is rough­ly a copy & paste of the exam­ple on the [vue-con­fet­ti GitHub repo](https://github.com/alexandermendes/vue-confetti):

```

<template>
    <main>
    </main>
</template>

<script>
    import Vue from 'vue'
    import VueConfetti from 'vue-confetti'

    Vue.use(VueConfetti);

    export default {
        mounted: function() {
            this.$confetti.start({
                shape: 'heart',
                colors: ['DodgerBlue', 'OliveDrab', 'Gold', 'pink', 'SlateBlue', 'lightblue', 'Violet', 'PaleGreen', 'SteelBlue', 'SandyBrown', 'Chocolate', 'Crimson'],
            });
            setTimeout(() => {
                this.$confetti.stop();
            }, 5000);
        },
        methods: {}
    }
</script>

```

Unfor­tu­nate­ly, this did­n’t work out of the box on Vue.js 3, giv­ing us the error:

<kbd>Uncaught TypeError: Cannot read property '$confetti' of undefined</kbd>

So to fig­ure out what was wrong here, I had a look at the [Vue plu­g­in](https://vuejs.org/v2/guide/plugins.html) <kbd>VueConfetti</kbd> we’re import­ing, which looks like this:

```

import Confetti from './confetti';

export { Confetti };

export default {
  install(Vue, options) {
    if (this.installed) {
      return;
    }
    this.installed = true;
    Vue.prototype.$confetti = new Confetti(options); // eslint-disable-line no-param-reassign
  },
};

```

The way plu­g­ins work is that they define an <kbd>install()</kbd> func­tion that is called to do what­ev­er they need to do to install them­selves, when <kbd>Vue.use()</kbd> is called.

In Vue.js 2, the glob­al <kbd>Vue</kbd> con­struc­tor is passed in as the first para­me­ter, but in Vue.js 3 we’d actu­al­ly be call­ing <kbd>app.use()</kbd>, and the first para­me­ter then becomes the app con­text, which is not a con­struc­tor, and thus has no <kbd>.prototype</kbd>.

Indeed, if we <kbd>console.log()</kbd> the first para­me­ter passed in via Vue.js 2, we’ll see the Vue constructor:

```

ƒ Vue (options) {
  if ( true &&
    !(this instanceof Vue)
  ) {
    warn('Vue is a constructor and should be called with the `new` keyword');
  }
  this._init(options);
}

```

But a <kbd>console.log()</kbd> the first para­me­ter passed in via Vue.js 3, we’ll see the app context:

```

{_component: {…}, _props: null, _container: null, _context: {…}, …}
component: ƒ component(name, component)
config: (...)
directive: ƒ directive(name, directive)
mixin: ƒ mixin(mixin)
mount: (containerOrSelector) => {…}
provide: ƒ provide(key, value)
unmount: ƒ unmount()
use: ƒ use(plugin, ...options)
_component: {components: {…}, data: ƒ}
_container: null
_context: {config: {…}, mixins: Array(0), components: {…}, directives: {…}, provides: {…}}
_props: null
get config: ƒ config()
set config: ƒ config(v)
__proto__ : Object

```

So okay, how can we fix this? The prob­lem is that Vue­Con­fet­ti is try­ing to inject a glob­al­ly shared instance prop­er­ty <kbd>$confetti</kbd> via <kbd>Vue.prototype.$confetti</kbd>, but don’t have a glob­al con­struc­tor in Vue.js 3, so <kbd>.prototype</kbd> isn’t a thing here.

One way would be to change the <kbd>vue-confetti/index.js</kbd> code to use the new app instance’s <kbd>config.globalProperties</kbd> to accom­plish the same thing, some­thing like:

<kbd>app.config.globalProperties.$confetti = new Confetti(options);</kbd>

c.f.: [Attach­ing Glob­al­ly Shared Instance Properties](https://github.com/vuejs/rfcs/blob/master/active-rfcs/0009-global-api-change.md#attaching-globally-shared-instance-properties)

But this would require chang­ing the Vue­Con­fet­ti code via a fork/​pull request. While I’m not against doing this, I real­ized there was an eas­i­er way to accom­plish the same thing:

```

<template>
</template>

<script>
    import Confetti from 'vue-confetti/src/confetti.js';
    export default {
        data: () => ({
            confetti: new Confetti(),
        }),
        mounted: function() {
            this.confetti.start({
                shape: 'heart',
                colors: ['DodgerBlue', 'OliveDrab', 'Gold', 'pink', 'SlateBlue', 'lightblue', 'Violet', 'PaleGreen', 'SteelBlue', 'SandyBrown', 'Chocolate', 'Crimson'],
            });
            setTimeout(() => {
                this.confetti.stop();
            }, 5000);
        },
        methods: {}
    }
</script>

```

Here we change the <kbd>Confetti.vue</kbd> com­po­nent to direct­ly import <kbd>'vue-confetti/src/confetti.js'</kbd> and assign the new <kbd>Confetti()</kbd> object to our local data state object, rather than hav­ing it be glob­al­ly available.

This feels a lit­tle nicer to me in gen­er­al, because there’s prob­a­bly no great rea­son for the <kbd>$confetti</kbd> object to be glob­al­ly avail­able, if we’re cre­at­ing a <kbd>Confetti.vue</kbd> com­po­nent that can nice­ly encap­su­late it.

## Should you use Vue.js 3 now?

We decid­ed to use Vue.js 3 now, but should you?

I think much depends on how heav­i­ly you lean on third par­ty com­po­nents, plu­g­ins, and mixins.

<aside>
                                The more code you’ll be writ­ing your­self, the safer it is to use Vue.js <span>3</span> now
                            </aside>

While all soft­ware always has issues, Vue.js 3 itself seems quite sol­id, and the first-par­ty pack­ages like [Vuex](https://vuex.vuejs.org/) and [Vue-Router](https://router.vuejs.org/) are com­ing along great.

There will like­ly be some lag in third par­ty pack­ages get­ting updat­ed for Vue.js 3, and some may nev­er be.

Thus whether to go with Vue.js 3 now real­ly depends on how much you rely on said third par­ty packages.

For us, the ben­e­fits are worth­while enough for us to begin learn­ing and using Vue.js 3 now.

## Wrap­ping up

Hope­ful­ly this small dive into what it looks like updat­ing your code for Vue.js 3 is help­ful to you. While it is rel­a­tive­ly nar­row in scope, it does touch on some top­ics I had­n’t seen cov­ered else­where, at least not wrapped up in a neat package.

![Wrapped up in a bow](https://nystudio107-ems2qegf7x6qiqq.netdna-ssl.com/img/blog/_992x558_crop_center-center_82_line/wrapped-up-in-a-bow.jpg)

I’m excit­ed to explore Vue.js 3 fur­ther, and will very like­ly doc­u­ment more of my jour­ney learn­ing the new hot­ness in Vue.js 3.

Hap­py coding!

## Further Reading

_If you want to be notified about new articles, follow [nystudio107](https://twitter.com/nystudio107) on Twitter._

<small>Copyright ©2020 nystudio107. Designed by nystudio107</small>