What's New in Vue 3
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:
- Instead of exporting a class, Vue 3 exports a POJO for better integration with destructuring
import
statements. So instead of callingnew Vue()
, you should useVue.createApp()
. Otherwise you'll get aTypeError: Vue is not a constructor
error. $mount()
is now justmount()
. 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-levelapp
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 prefixmount()
as$mount()
to avoid conflicting with yourdata
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 {};
}
});