Vue.js

What’s New in Vue.js 3?

postData.title

Minh Huan Ngo

Since the release of Vue.js 2, the Vue.js community has grown rapidly and has received great reviews from users. Along with Angular and React, it is one of the most popular JavaScript frameworks. Vue.js 3 is about to be released and has caused quite a hype in the world of web development in the last few months.

Improvements

The new version of Vue.js is designed for speed. Although Vue 2 was already relatively fast, Vue 3 has up to 100% better update performance and up to 200% faster server-side renderings. Component initialization is also now more efficient, even with compiler-informed fast paths to execution. The virtual DOM has also been completely rewritten and outperforms its old version in terms of speed.

There are also improvements in terms of size. Vue 2 was already pretty darn small with a weight of about 20 kB gzipped. With a constant baseline size of <10 kB gzipped, Vue 3 weighs only half as much. How? Mostly by eliminating unused libraries (tree shaking). So if you don’t use an item, it’s not included.

Vue 3 will support TypeScript. Besides, the packages will be decoupled, which will make everything more modular and easier to maintain.

Building native applications with Vue isn’t all that difficult, but with Vue 3 the runtime core is platform-independent, which makes it even easier to use this technology with any type of platform.

As you can see, it’s worth taking a look at what new features will be at Vue 3.

Composition API

So far, we used the Options API when creating new components. This API forced us to separate the component’s code by options, which meant we had all reactive data in one place (Data), all calculated properties in one place (Computed) and all methods in one place (Methods).

Although it is manageable and readable for smaller components, it becomes painful when the component becomes more complex and has to do with multiple functionalities. Usually, the logic related to a particular functionality contains some reactive data, calculated properties, a method, or a few thereof; sometimes it also involves the use of component lifecycle hooks. As a result, when working on a single logical issue, you have to keep jumping back and forth between different options in the code.

The other problem you might have encountered while working with Vue is how to extract common logic that can be reused by multiple components. Vue already has few options for doing this, but each has its drawbacks (e.g. mixins and scoped slots).

Vue 3’s solution to this is a setup () method that allows us to use the composition syntax. Every piece of logic outside of this method is defined as a compositional function.

Code example from a color-picking game using the composition API:

<script>
import Vue from "vue";
import ColorChoiceButton from "@/components/ColorChoiceButton";
import GameControlHeader from "@/components/GameControlHeader";
import { reactive } from "vue";
import { getRandomColor } from "@/utils/getRandomColor";
 
export default {
  components: {
    GameControlHeader,
    ColorChoiceButton
  },
  setup() {
    const score = reactive({
      lost: 0,
      won: 0
    });
 
    const colors = reactive({
      picked: "",
      right: "",
      choices: ["", "", ""]
    });
 
    function pickColor(color = "") {
      colors.picked = color;
      if (colors.picked === colors.right) {
        //round won
        score.won++;
      } else {
        //round lost
        score.lost++;
      }
    }
    function setFreshColors() {
      colors.choices[0] = getRandomColor();
      colors.choices[1] = getRandomColor();
      colors.choices[2] = getRandomColor();
 
      // we should detect the rare case that we get the same color twice and generate colors again
 
      const randomChoice = Math.round(Math.random() * 2);
      colors.right = colors.choices[randomChoice];
      colors.picked = "";
    }
    function resetGame() {
      score.won = 0;
      score.lost = 0;
      setFreshColors();
    } //new colors, score = 0
 
    resetGame();
 
    return { pickColor, resetGame, setFreshColors, score, colors };
  }
};
</script>

In short, it’s just a function that returns properties and functions to the template. We declare all reactive properties, computed properties, watchers and lifecycle hooks here and then return them so that they can be used in the template.

The Composition API is a great way to make code more readable and maintainable. It will help make larger Vue projects more modular and reusable; a very promising sign of the developer-friendly changes the Vue team is making. It seems like they discovered many pain points from the development teams and tried to come up with workable solutions without making extremely drastic changes.

Multiple root elements

In Vue 2, the template tag can only hold one root element. Even if we only have two

– Tags, we’d have to put them in one

– Tag to make it work. 

Therefore we also had to change the CSS code in the parent component to look as expected.

In Vue 3, this limitation has been removed. A root element is no longer required.

We can use any number of tags right inside the section <template></template>:

<template>
  <p> Count: {{ count }} </p>
  <button @click="increment"> Increment </button>
  <button @click="decrement"> Decrement</button>
</template>

Suspense

Suspense is a component needed during lazy loading, its main purpose being to encase lazy components. Several lazy components can be wrapped with the suspense component. Version 3 of Vue.js introduces Suspense to wait for nested asynchronous dependencies in a nested tree, and it works well with asynchronous components. Instead of our component, fallback content is displayed until the desired condition is met. The condition is usually an asynchronous process that takes place within our component setup function.

Example:

<Suspense>
  <template >
    <suspended-component />
  </template>
  <template #fallback>
    Loading...
  </template>
</Suspense>

Portals

The component moves all children to the first element in the DOM tree described by the target. The target works like a conventional query selector, i.e. an element can be selected via the ID (#target), the class (.target) or the tag name (e.g. main).

With teleports, elements placed in front of the content such as modals, (cookie) banners, pop-ups, etc. can be called from anywhere in the app, but they always render at the same place in the DOM tree.

App.vue

<Component>
  <teleport to="#target">
    <div>...</div>
  </teleport>
</Component>

index.html

<body>
  ...
  <div id="app"></div>
  <div id="portal-target"></div>
</body>

Multiple V-Models

If you use Vue, you already know that v-models are used for bidirectional data binding of Vue components. In Vue 2, you get a v-model for a component, but there’s great news in this new version!

You can now have as many v-model declarations and bindings per component as you want and also name the individual v-models.

Something like this is now possible:

<InviteeForm
   v-model:name="inviteeName"
   v-model:email="inviteeEmail"
/>

Global mounting/configuration API change

We can find another major change in the way we instantiate and configure our application. We are currently using the global Vue object to deploy any configuration and create new Vue instances. Any change made to the Vue object will affect every Vue instance and component.

Now let’s look at how it will work in Vue 3:

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')

Conclusion

Aside from the Composition API, which is the biggest new API in Vue 3, we can also find a lot of smaller improvements. We can see that Vue is moving towards a better experience for developers and simpler, more intuitive APIs. It’s also great to see that the Vue team has decided to incorporate many ideas that are currently only available through third-party libraries into the core of the framework.