By Filip Rakowski

Crazy tech nerd

Original text: vueschool. IO/articles/vu…

Reproduction without permission is strictly prohibited

In the last article, we looked at the performance improvements that Vue 3 will bring. We already know that programs written with the new Vue 3 will work well, but performance is not the most important part. What matters most to developers is how the new release will affect the way we write code.

As you might expect, Vue 3 brings a lot of exciting new features. Thankfully, the Vue team mainly introduces additions and improvements to the current API, rather than major changes, so people who already know Vue 2 should soon be happy with the new syntax.

Let’s start with the API that most of you probably heard about… Let’s start with an API that most people have probably heard of…

Composition API

The component API is the most discussed and featured syntax in the next major release of Vue. This is a whole new approach to logical reuse and code organization.

Currently, we use the so-called Options API to build components. To add logic to the Vue component, we populate (optional) properties such as Data, Methods, computed, and so on. The biggest drawback to this approach is that it is not valid JavaScript code. You need to know exactly what properties are accessible in the template and the behavior of the this keyword. Behind the scenes, the Vue compiler needs to convert this property into working code. So we don’t benefit from automated recommendations or type checking.

The component API aims to solve this problem by exposing the mechanisms currently available in component properties as JavaScript functions. The Vue core team describes the component API as “a set of additional function-based apis that allow for the flexibility to compose component logic.” Code written with component apis is more readable, and there’s no magic behind it, so it’s easier to read and learn.

Let’s look at a simple example of a component using the new component API to see how it works.

<template> <button @click="increment"> Count is: {{ count }}, double is {{ double }}, click to increment. </button> </template> <script> import { ref, computed, onMounted } from 'vue' export default { setup() { const count = ref(0) const double = computed(() => count.value * 2) function increment() { count.value++ } onMounted(() => console.log('component mounted! ')) return { count, double, increment } } } </script>Copy the code

Now, let’s break the code down into pieces to see what happens:

import { ref, computed, onMounted } from 'vue'
Copy the code

As I mentioned earlier, component apis expose component properties as functions, so the first step is to import the required functions. In the example, you need to use REF to create the responsive reference, computed to create the computed attributes, and onMounted to access the installed lifecycle hook.

Now you’re probably wondering what this arcane setup method is.

export default {
  setup() {
Copy the code

In short, it’s just a function that returns properties and functions to the template. Here we declare all the responsive properties, computed properties, observers, and lifecycle hooks, and return them so that they can be used in the template.

What we do not return from the setup function will not be available in the template.

const count = ref(0)
Copy the code

Based on the above, we declare a response property named count with a ref function. It can wrap any primitive or object and return a responsive reference to it. The value of the passed element is retained in the value attribute of the created reference. For example, if you want to access the value of the count reference, you need to explicitly ask for count.value.

const double = computed((a)= > count.value * 2)

function increment() {
  count.value++
}
Copy the code

This is exactly what we do when we declare the double and increment functions that evaluate the properties.

onMounted((a)= > console.log('component mounted! '))
Copy the code

With onMounted Hook, we will log some messages when installing components, just to show you that it can be done! 😉

return {
  count,
  double,
  increment
}
Copy the code

Finally, we’ll use the increment method to return the count and double properties to make them available in the template.

<template>
  <button @click="increment">
    Count is: {{ count }}, double is {{ double }}. Click to increment.
  </button>
</template>
Copy the code

Look! Now we can access the properties and functions returned by the setup method in the template just as if we had declared them through the old Options API.

This is a simple example that can be easily implemented using the Options API. The real benefits of the new component API are not only the ability to code differently, but also the ability to reuse our code and logic.

Code reuse with component apis

The new component API has more advantages. Think about code reuse. Currently, if we want to share some code between other components, we have two options available: mixins and scoped slots. But both have drawbacks.

Suppose we want to extract functionality from counter and reuse it in other components. Below, you can see how it works with the available apis and the new component apis:

Let’s start with mixins:

import CounterMixin from './mixins/counter'

export default {
  mixins: [CounterMixin]
}
Copy the code

The biggest drawback to mixins is that we don’t know anything about the behavior they actually add to a component. Not only does this make the code difficult to understand, but it can also cause names to conflict with existing properties and functions.

Here are the scope slots:

<template>
  <Counter v-slot="{ count, increment }">
     {{ count }}
    <button @click="increment">Increment</button> 
  </Counter> 
</template>
Copy the code

By using scoped slots, we know exactly which properties are accessible through the V-slot attribute, so the code is easier to understand. The downside of this approach is that we can only access it from within the template and only use it within the scope of the Counter component.

Now it’s time to use the component API:

function useCounter() {
  const count = ref(0)
  function increment () { count.value++ }

  return {
    count,
    incrememt
  }
}

export default {
  setup () {
    const { count, increment } = useCounter()
    return {
      count,
      increment
    }
  }
}
Copy the code

Is it more elegant? We are not limited by template and component scope, and we know exactly which properties are accessible from counter. In addition, we can benefit from the code completion available in the editor, because useCounter is just a function that returns some property, so the editor can help us with type checking and suggestions.

This is also a more elegant way to use third-party libraries. For example, if we wanted to use Vuex, we could explicitly use the useStore function instead of polluting the Vue prototype (this.$store). This approach also removes the magic behind the Vue plug-in.

const { commit, dispatch } = useStore()
Copy the code

If you want to learn more about component apis and their use cases, I strongly encourage you to read this article from the Vue team, which explains the reasoning behind the new API and suggests the best use cases. There is also Great Repository, which contains examples of component apis used by Thorsten Lunborg from the Vue core team.

Global mount/configuration API changes

Another significant change can be found in the way the program is instantiated and configured. Let’s see how it works now:

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')
Copy the code

Currently, we are using global Vue objects to provide all configurations and create new Vue instances. Any changes made to a Vue object affect every Vue instance and component.

Now, let’s see how it works 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')
Copy the code

You may have noticed that each configuration is limited to a certain Vue program defined using createApp.

It makes your code easier to understand and less prone to unexpected problems caused by third-party plug-ins. Currently, if some third party solution is modifying Vue objects, it can affect your program in unexpected ways (especially global blending), which Vue 3 does not have.

Changes to this API are currently being discussed in this RFC, which means that they may change in the future.

Fragments

Another exciting additional feature we can expect in Vue 3 is snippet.

What clip, you might ask? Well, if you create a Vue component, it can only have one root node.

This means that you cannot create a component like this:

<template>
  <div>Hello</div>
  <div>World</div>
</template>
Copy the code

The reason is that a Vue instance representing any Vue component needs to be bound to a single DOM element. The only way to create a component with multiple DOM nodes is to create a functional component with no underlying Vue instance.

The React community, it turns out, has the same problem. Their solution is a virtual element called a Fragment. It looks like this;

class Columns extends React.Component {
  render() {
    return (
      <React.Fragment>
        <td>Hello</td>
        <td>World</td>
      </React.Fragment>); }}Copy the code

Although the Fragment looks like a normal DOM element, it is virtual and does not render in the DOM tree at all. This allows you to bind component functionality to a single element without creating redundant DOM nodes.

Now you can use fragments in VUE 2 with the Vue-Fragments library, and in VUE 3 you can use them directly!

Suspense

Another feature learned from React that will be used in Vue 3 is the Girl component.

Suspense can pause your component rendering and render the backup component until the condition is met. While at Vue London, Yuyuxi touched briefly on the subject and showed us what to expect from the API. Suspense, it turns out, is just a component with slots:

<Suspense>
  <template >
    <Suspended-component />
  </template>
  <template #fallback>
    Loading...
  </template>
</Suspense>
Copy the code

The backup content will be displayed until the Suspended component is fully rendered. The suspend can wait until the component is downloaded (if it is asynchronous), or some asynchronous operation is performed in the setup function.

Multiple v-models

V-model is a directive that can be used to implement bidirectional binding on a given component. We can pass the responsiveness attribute and modify it from within the component.

We can get a good idea of v-Model from the form elements:

<input v-bind="property />
Copy the code

But did you know that you can use v-Model for every component? Internally, v-Model is just a shortcut to passing the value attribute and listening for input events. Rewriting the above example with the following syntax will have exactly the same effect:

<input 
  v-bind:value="property"
  v-on:input="property = $event.target.value"
/>
Copy the code

We can even use the component Model property to change the default property and event name:

model: {
  prop: 'checked',
  event: 'change'
}
Copy the code

As you can see, the V-Model directive can be a very useful syntax if we want two-way binding in a component. Unfortunately, there can only be one V-Model per component.

Fortunately, this won’t be a problem in Vue 3! You will be able to give v-Model property names and have as many property names as you need. In the following example, you can find two V-Models in the form component:

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

Currently, changes to this API are being discussed in this RFC, which means there may be changes in the future.

Portals

Portals are special components used to render something outside of the current component. It’s also one of the features implemented in React. Here’s what the React documentation says about Portals:

“Portals provide a unique way to render children into DOM nodes outside of the parent component’s DOM hierarchy.”

This mode of processing is a great way to use pop-up Windows and components that are usually displayed at the top of the page. Using Portals, you can ensure that no host component CSS rules will affect what you’re displaying, and you can avoid hacking with z-Index.

For each Portal, we need to specify a target location where the Portals content will be rendered. Below, you can see the implementation from the Portal-Vue library, which adds this functionality to VUE 2:

<portal to="destination">
  <p>This slot content will be rendered wherever thportal-target with name 'destination'
    is  located.</p>
</portal>

<portal-target name="destination">
  <! -- This component can be located anywhere in your App. The slot content of the above portal component wilbe rendered here. -->
</portal-target>
Copy the code

Vue 3 provides out-of-the-box support for Portals!

New custom instruction API

The custom instruction API will be slightly changed in Vue 3 to better align with the component lifecycle. This improvement should make the API more intuitive, making it easier for novices to understand and learn the API.

Here is the current custom directive API:

const MyDirective = {
  bind(el, binding, vnode, prevVnode) {},
  inserted() {},
  update() {},
  componentUpdated() {},
  unbind() {}
}
Copy the code

This is what it looks like in Vue 3.

const MyDirective = {
  beforeMount(el, binding, vnode, prevVnode) {},
  mounted() {},
  beforeUpdate() {},
  updated() {},
  beforeUnmount() {}, // new
  unmounted() {}
}
Copy the code

Even if this is a significant improvement, it should be easily covered by the Vue compatible version.

Changes to the API are discussed in this RFC, which means that improvements are possible in the future.

Abstract

In addition to the Composition API, which is the largest major new API in Vue 3, there are many smaller improvements to be found. You can see Vue moving toward a friendlier development experience and a simpler, more intuitive API. It’s great to see that the Vue team has decided to put at the heart of the framework many of the ideas that are currently only available through third-party libraries.

The list above represents only the major API changes and improvements. If you’re curious about anything else, be sure to check out the Vue RFCs repository.

Welcome to pay attention to the front public number: front pioneer, free to receive Webpack tutorials.