What's New in Vue 3

Dec 7, 2020

Vue 3 was released on September 18, 2020. Vue 3 includes numerous improvements, including a more modular core, the composition API, and numerous performance improvements. However, even with these new improvements, it still feels like the same old Vue that we know and love, just with a few new delightful surprises. Here's how you can get started.

Hello, Vue 3

Like Vue 2.x, you can install Vue from npm, or you can load Vue from a CDN. To install Vue 3 from npm, run:

npm install vue@3.x

You can also load Vue with a <script> tag from a CDN like unpkg.

<script src="https://unpkg.com/vue@3.x"></script>

The basic JavaScript syntax of Vue hasn't changed much. The little "row, row, row your boat" example that we used for Vue 2 works in Vue 3, after a couple minor changes:

<div id="content" style="border: 1px solid #ddd; padding: 1em"></div>

<script src="https://unpkg.com/vue@3.x"></script>
<script>
  Vue.createApp({
    data: () => ({ message: 'Row' }),
    template: `
    <div>
      <h1>{{message}} your boat</h1>
      <button v-on:click="message += ' row'">Add</button>
    </div>
    `
  }).mount('#content');
</script>

Below is a live example:

We needed to make 2 changes to make this app work in Vue 3:

  1. Instead of exporting a class, Vue 3 exports a POJO for better integration with destructuring import statements. So instead of calling new Vue(), you should use Vue.createApp(). Otherwise you'll get a TypeError: Vue is not a constructor error.
  2. $mount() is now just mount(). This 1 character change is the result of a much more important change: Vue 3 has a separate notion of an "app instance". Unlike in Vue 2, where both your top-level app and your components were instances of the same class, Vue 3 has a separate notion of an app. That means there's no need to prefix mount() as $mount() to avoid conflicting with your data properties.

Server Side Rendering

The number one reason why we are so bullish on Vue is Vue's versatility. Vue largely "just works" in Node.js or in the browser; with vanilla HTML, SFC, or JSX; with render() functions or templates; with outputting vanilla HTML or mounted on a DOM.

In Vue 2, you could take an existing Vue instance and render to an HTML string in Node.js using vue-server-renderer. With Vue 3, the syntax has changed slightly, but the general idea is similar. Instead of vue-server-renderer, you should now use @vue/server-renderer:

npm install vue@3.x @vue/server-renderer@3.x

The @vue/server-renderer package exports a renderToString() function that you can use to render a Vue app:

const { createSSRApp } = require('vue');
const { renderToString } = require('@vue/server-renderer');

const app = createSSRApp({
  data: () => ({ to: 'World' }),
  template: '<h1>Hello, {{to}}</h1>'
});

void async function main() {
  const html = await renderToString(app);
  console.log(html); // "<h1>Hello, World</h1>"
}();

Note that this example uses Vue 3's new createSSRApp() function, not createApp(). That is Vue's recommended approach for server-side rendering, but it is not strictly necessary for static sites because the major difference between createApp() and createSSRApp() is support for client-side hydration.

For example, the below script works identically to the above script, even though it uses createApp() rather than createSSRApp().

const { createApp } = require('vue');
const { renderToString } = require('@vue/server-renderer');

const app = createApp({
  data: () => ({ to: 'World' }),
  template: '<h1>Hello, {{to}}</h1>'
});

void async function main() {
  const html = await renderToString(app);
  console.log(html); // "<h1>Hello, World</h1>"
}();

Introducing the Composition API

The Composition API is a fairly complex set of tools that makes it easier to reuse logic with Vue components. The Composition API starts with the new setup() function on components. The setup() function is the "entry point" for your component.

For example, here's how you can create a "Hello, World" component in Vue 3:

const app = createApp({
  data: () => ({ to: 'World' }),
  template: '<hello v-bind:to="to"></hello>'
});

app.component('hello', {
  props: ['to'],
  // "<h1>Hello, World</h1>"
  template: '<h1>Hello, {{to}}</h1>'
});

The setup() function lets you do all sorts of things that would require defining properties on the Vue instance in Vue 2, like defining reactive properties or registering lifecycle hooks.

For example, you can add new properties that are accessible from your templates by returning an object from the setup() function:

const app = createApp({
  data: () => ({ to: 'World' }),
  template: '<hello v-bind:to="to"></hello>'
});

app.component('hello', {
  props: ['to'],
  // "<h1>Hello, WORLD</h1>"
  template: '<h1>Hello, {{toUpper}}</h1>',
  setup: function(props) {
    return { toUpper: props.to.toUpperCase() };
  }
});

The Vue global also has helper functions like onMounted() and onErrorCaptured() that let you register lifecycle hooks from the setup() function. These functions don't overwrite existing lifecycle hooks, which means you can easily define multiple hooks for the same component lifecycle event.

const app = Vue.createApp({
  data: () => ({ to: 'World' }),
  template: '<hello v-bind:to="to"></hello>'
});

// Prints 'Mounted from component!' followed by 'Mounted from setup!'
app.component('hello', {
  props: ['to'],
  template: '<h1>Hello, {{to}}</h1>',
  mounted: function() {
    console.log('Mounted from component!');
  },
  setup: function(props) {
    Vue.onMounted(() => console.log('Mounted from setup!'));
    return {};
  }
});

Vue School has some of our favorite Vue video courses. Their Vue.js Master Class walks you through building a real world application, and does a great job of teaching you how to integrate Vue with Firebase. Check it out!


Did you find this tutorial useful? Say thanks by starring our repo on GitHub!

More Vue Tutorials