What’s New in Vue 3
Vue 3 was officially released on September 18, 2020. While it has some great new features, it’s also completely backward-compatible with Vue 2. The additional features are just that — additional and designed to improve the quality of a developer's life and give us new and varied ways of creating our frontends.
New Features in Vue 3
- Composition API
- Full TypeScript support
- Portals
- Fragments
- Suspense
- Global Mounting/Configuration API change
- Multiple v-models
- Custom Directive API
If you aren't quite ready to upgrade to Vue 3, some of the new Vue 3 features are available as plugins for Vue 2, and I will mention these when we get there. In Vue 3, these features are built-in, which gives developers a better instant experience with Vue (those that have worked with Vue understand the little issues we deal with).
Vue is not “just another framework.” Vue is designed as an interface framework for web applications, to help you create the “view” for your application. It can be used with React and Angular, which are monolithic frameworks, to supply the UI while behind the scenes you have Angular and React doing the rest of the work.
The Composition API
The Composition API is an alternative to the current Vue Options API. It’s designed to have Typescript support in mind, as well as code reuse, maintainability, and the amalgamation of functionality by logical concern.
How does this compare to the Options API? Using the Options API in a simple component can get very messy very quickly as you have data, methods, props, components, computed values, and more. Each section could contain multiple sections of functionality — and now they are spread between the different sections of your code.
What could that look like in a data grid style component? The example below only shows the options and a list of what can — and probably would — be found in each section. If you were to write this component in full, that would be a large chunk of code.
<script>
export default {
data () {
return {
//Properties for data
//Properties for filtering
//Properties for sorting
//Properties for paging
}
},
methods: {
//Methods for data
//Methods for filtering
//Methods for sorting
//Methods for paging
},
computed: {
//Values for data
//Values for filtering
//Values for sorting
//Values for paging
}
}
</script>
As you can see, functionality for each logical segment is split between the separate options of the Options API.
The Composition API makes this more maintainable and readable. It introduces the setup method, which works as follows in a sample script section where we're doubling a number.
<script>
import { ref } from "@vue/composition-api"
export default {
setup () {
const theNumber = ref(1)
function doubleTheNumber () {
theNumber.value = parseInt(theNumber.value) * 2
}
return {
theNumber,
doubleTheNumber
}
}
}
</script>
Does this mean you're going to end up with one huge setup method on all of your components? No, it doesn’t. You can use composition functions elsewhere (in other files) so as not to bloat your setup method. This has the distinct advantage of code separation and reuse. The composition function can be used in any number of components.
Joel Parks covers Getting Started with the Vue 3 Component API in another article.
Full TypeScript Support
Vue 3 comes with full TypeScript support. Vue 2 was able to accommodate TypeScript — you could shoehorn it into your Vue 2 application. It wasn’t the best arrangement, and in a lot of cases looked hacked together.
Vue 3 is written in TypeScript with auto-generated type definitions and the API that is the same in both TypeScript and JavaScript.
Portals
Portals are a concept from React. A Portal does what it says on the tin: it’s responsible for rendering something in a place different from where it’s declared. In the Vue 3 case, a Portal allows you to render a component, or part of a component, in a different place in the DOM tree.
Portals are handy for everything: modals, notifications, pop-ups, and so on. You want all of those items to appear above everything else, outside the main elements that constitute the page body. With Portals, you don’t need to have messy CSS, z-indexing that doesn’t make sense, or elements appearing in other random places on the page.
Portals are available in Vue 2 via third-party plugins — portal-vue is one example.
To use a Portal in your application, you will need a tag with an Id. An example would be to have a <div>
with the Id of a modal just before the end of your HTML body.
<!DOCTYPE html>
<html lang="en">
<head>
</head>
<body>
<noscript>
<strong>We're sorry but this app doesn't work properly without JavaScript enabled. Please enable it to continue.</strong>
</noscript>
<div id="app"></div>
<div id="modal"></div>
<!-- built files will be auto injected -->
</body>
</html>
Next, whenever you want to teleport a component, or part of a component, into the “modal,” you use the Portal syntax.
<template>
<Portal target="#modal">
<div>I'm rendered in the modal, honest, I will be!</div>
<Portal>
</template>
Fragments
Another concept coming from React is Fragments — in some circles, it is also called “Multiple Root Nodes.” In Vue 2, you are only allowed one root element in a component. Fragments address this and allow you to, well, use more than one root node in a component.
Multiple root nodes are as simple as the sound. The only thing to watch out for is that if you want to use attribute inheritance, you'll have to specify which of the root components should inherit any attributes passed to the parent component:
<template>
<ComponentOne />
<ComponentTwo v-bind="$attrs"/>
<ComponentThree />
</template>
As you can see, Vue 3 lets you start using multiple root nodes right away without needring to do any extra work.
Suspense
Suspense is a special component that renders a fallback content instead of your component until a condition is met.
You might remember all those times you’ve written components and automatically added in v-if="!isLoading"
into the root element and a v-else on the second element to render while content loads, for example, an API or other asynchronous call.
This is what Suspense is for. The template below shows an example syntax, and it’s a lot nicer than the v-if version.
<Suspense>
<template #default>
<MyData />
</template>
<template #fallback>
<div>Loading...</div>
</template>
</Suspense>
How does it detect that your component should be loaded (rather than had been loaded)? This comes with the Composition API and the setup method discussed earlier. If you make the setup method async, Suspense takes over and, once you return from setup, the fallback is displayed.
Something like the snippet below would trigger the fallback until you have completed the call to getMyData.
export default {
async setup () {
const data = await getMyData()
return { data }
}
}
This feature drastically — and elegantly — reduces the boilerplate code required to make external calls.
Global Mounting/Configuration API Change
Mounting your Vue app has changed slightly, and in ways that improve the possibilities of what can be done: moving from a single global instance to declaring an instance and mounting it.
The following are the current and new syntax snippets taken from the RFC in the vuejs repository.
CURRENT:
import Vue from 'vue'
import App from './App.vue'
Vue.config.ignoredElements = [/^app-/]
Vue.use(/* ... */)
Vue.mixin(/* ... */)
Vue.component(/* ... */)
Vue.directive(/* ... */)
new Vue({
render: h => h(App)
}).$mount('#app')
NEW:
import { createApp } from 'vue'
import App from './App.vue'
const app = createApp(App)
app.config.ignoredElements = [/^app-/]
app.use(/* ... */)
app.mixin(/* ... */)
app.component(/* ... */)
app.directive(/* ... */)
app.mount('#app')
Does this bring in the possibility of multiple apps? Maybe. But it's clear there isn’t a single instance and you can do much more with the new approach.
Multiple v-Models
If you are familiar with Vue, you most likely know what a v-model is. It’s a directive to achieve two-way binding on a component. You use this to pass a reactive property and modify from within. What you may not know is that v-model is just a shortcut to passing in value and listening to the input event.
So what does “multiple v-models” mean? You've probably had a situation where you wanted to bind and listen to multiple properties in your components. For example, you could create a Name component that includes two values: Forename(s) and Surname. You want to have a v-model for each of those values – and this is where the multiple v-models kick in.
<NameComponent
v-model:fornames="forname"
v-model:surname="surname"
/>
This new syntax seems easy, right? It is, and now you can, essentially, name your v-models.
Custom Directive API
From using Vue, you should know what a directive is. One of the main built-in directives is the one we’ve shown when talking about v-model. There are also others, including the likes of v-show.
You can create custom directives in Vue 2. However, in Vue 3, they change. The question is why? Well, in the current form the Directive API has its own set of lifecycle methods, which don’t align with the normal Vue lifecycle. Bind, inserted, update, componentUpdated, and unbind.
In Vue 3, these methods are synced with the standard Vue lifecycle, so you now get beforeMount, mounted, beforeUpdate, updated, beforeUnmount, and unmounted. Much easier to remember when building your application. There will no longer be digging around, trying to find which method you need, and remembering two different lifecycles.
The Future of the Vue Framework
Now that it has been officially released, you can dive in and start using Vue 3 right away. Its new features help us develop better and faster systems. There are quality of life improvements, as well as more ways for you to create applications, which helps us excel in, and enjoy, our profession.
The thing to remember is that Vue 3 is backward-compatible (with some minor code changes). It doesn’t cancel the existing way of doing things; instead, it adds new ways.
If you are already using Vue 3, why not try it with Wijmo – a UI component library that is available in a Vue version.