Vue.js 3 Cookbook
上QQ阅读APP看书,第一时间看更新

Creating components with multiple root elements

In Vue 3, it is possible to create components with multiple root elements, without the need for a wrapping element. This option is also known as a fragment.

In React, this has been possible for a long time, but in Vue, you need to use custom third-party plugins such as vue-fragment (https://github.com/Thunberg087/vue-fragment) to use this feature.

In this recipe, you will learn how to create a component with multiple root elements, and how it could be used with a <template> section and a render function.

How to do it...

In this recipe, we will create two examples of a multiple root element component, one with a <template> structure, and another with a render function. To do this, this recipe will be divided into two parts.

Creating the component with the <template> structure

In order to use the <template> structure in our example, we will be using the template property of the Vue object where we can pass a string or a template string as the value, which will be interpolated by the Vue script and rendered on the screen:

  1. Using the base example from the 'Creating the base file' section, create a new file named template.html and open it.
  2. In the empty <script> HTML element, create the constants defineComponent and createApp by object-destructuring the Vue global constant:
const {
defineComponent,
createApp,
} = Vue;
  1. Create a constant named component, defined as the defineComponent method, passing a JavaScript object as an argument with three properties: data, methods, and template:
const component = defineComponent({
data: () => ({}),
methods: {},
template: ``
});
  1. In the data property, define it as a singleton function, returning a JavaScript object, with a property named count and with the default value as 0:
data: () => ({
count: 0
}),
  1. In the methods property, create a property called addOne, which is a function that will increase the value of count by 1:
methods: {
addOne() {
this.count += 1;
},
},
  1. In the template property, in the template string, create an h1 HTML element with a title. Then, as a sibling, create a button HTML element with an event listener bound to the click event, triggering the addOne function when executed:
template: `
<h1>
This is a Vue 3 Root Element!
</h1>
<button @click="addOne">
Pressed {{ count }} times.
</button>
`
  1. Finally, call the createApp function, passing the component constant as an argument. Then, prototype chain the mount function and, as an argument of the function, pass the div HTML element id attribute, ("#app"):
createApp(component)
.mount('#app');

Creating the component with the render function

In order to use the <template> structure in our example, we will be using the template property of the Vue object, where we can pass a string or a template string as the value, which will be interpolated by the Vue script and rendered on the screen:

  1. Using the base example from the 'Creating the base file' section, create a new file named render.html and open it.
  1. In the empty <script> HTML element, create the constants of the functions that will be used using the object destructuring method, calling the defineComponent, h, and createApp methods from the Vue global constant:
const {
defineComponent,
h,
createApp,
} = Vue;
  1. Create a constant named component, defined as the defineComponent method, passing a JavaScript object as an argument with three properties: datamethods, and render:
const component = defineComponent({
data: () => ({}),
methods: {},
render() {},
});
  1. In the data property, define it as a singleton function, returning a JavaScript object with a property named count and with the default value as 0:
data: () => ({
count: 0
}),
  1. In the methods property, create a property called addOne, which is a function that will increase the value of count by 1:
methods: {
addOne() {
this.count += 1;
},
},
  1. In the render property, perform the following steps:
    • Create a constant named h1 and define it as the h function, passing 'h1' as the first argument, and the title that will be used as the second argument.
    • Create a constant named button, which will be the h function, passing "button" as the first argument, a JavaScript object with the property onClick with a value of this.addOne as the second argument, and the content of button as the third argument.
    • Return an array, with the first value as the h1 constant, and the second value as the button constant:
render() {
const h1 = h('h1', 'This is a Vue 3 Root Element!');
const button = h('button', {
onClick: this.addOne,
}, `Pressed ${this.count} times.`);

return [
h1,
button,
];
},
  1. Finally, call the createApp function, passing the component constant as an argument, prototype chaining the mount function, and passing the div HTML element id attribute, ("#app"), as an argument of the function:
createApp(component)
.mount('#app');

How it works...

The new Vue component creation API needs to be executed by a function, defineComponent, and the JavaScript object that is passed as an argument maintains almost the same structure as the old structure in Vue 2. In the examples, we used the same properties, data, render, methods, and template, all present in Vue 2.

In the example with the <template> structure, we didn't have to create a wrapper element to encapsulate the content of our application component and were able to have two root elements on the component directly.

In the render function example, the same behavior occurs, but the final example used the new exposed h API, where it is no longer a parameter of the render function. A breaking change was present in the example; in the button creation, we had to use the onClick property inside the data JavaScript object, not the on property, with the click method. This happens because of the new data structure of the VNode of Vue 3.