Using Vue 2:
There is no straight forward solution to this using Vue 2, but you can use Functional Components for this purpose, as functional components do not have the single-root limitation.
So first, create a my-span functional component which will be rendered in DOM with multiple nodes like:
<span>element 1</span>
<span>element 2</span>
using:
Vue.component('my-span', {
functional: true,
render: function (createElement, context) {
const span1 = createElement('span', 'element 1');
const span2 = createElement('span', 'element 2');
return [span1, span2]
},
})
You can create as many nodes you want, with any element you want and simply return that as an array.
In Vue 2.5.0+, if you are using single-file components, template-based functional components can be declared with:
<template functional>
</template>
Next, create a component just to wrap the <my-span> above like:
Vue.component('my-div', {
template: '<div><my-span /></div>'
})
Then using Vue’s <component> element with the is special attribute, we can dynamically switch between the <my-div> and <my-span> components like:
<component :is="condition ? 'my-div' : 'my-span'"></component>
This will result in the desired behaviour you are looking for. You can also inspect the rendered DOM to verify this.
Working Demo:
Vue.component('my-span', {
functional: true,
render: function (createElement, context) {
const span1 = createElement('span', 'element 1');
const span2 = createElement('span', 'element 2');
return [span1, span2]
},
})
Vue.component('my-div', {
template: '<div><my-span /></div>'
})
new Vue({
el: "#myApp",
data: {
condition: true
},
methods: {
toggle() {
this.condition = !this.condition;
}
}
})
#myApp{padding:20px}
#myApp div{padding:10px;border:2px solid #eee}
#myApp span{padding:5px;margin:5px;display:inline-flex}
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.min.js"></script>
<div id="myApp">
<button @click="toggle">Toggle</button><br>
<component :is="condition ? 'my-div' : 'my-span'"></component>
</div>
Using Vue 3:
In Vue 3, it would ve very easy to implement as we can have multiple root nodes in Vue 3, as you can see MySpan component has a template with multiple spans:
const MySpan = { template: '<span>element 1</span><span>element 2</span>' };
Working Demo:
const { createApp, ref } = Vue;
const MySpan = { template: '<span>element 1</span><span>element 2</span>' };
const MyDiv = {
components: { MySpan },
template: '<div><my-span /></div>'
};
const App = {
components: { MyDiv, MySpan },
setup() {
const condition = ref(true);
const toggle = () => {
condition.value = !condition.value;
};
return { condition, toggle };
}
};
createApp(App).mount("#myApp");
#myApp{padding:20px}
#myApp div{padding:10px;border:2px solid #eee}
#myApp span{padding:5px;margin:5px;display:inline-flex}
<script src="//unpkg.com/vue@next"></script>
<div id="myApp">
<button @click="toggle">Toggle</button><br>
<component :is="condition ? 'my-div' : 'my-span'"></component>
</div>
component isyou would be rendering two separate modules. Dynamic components right? So how does that reduce the additional mark up. It is just that they are in separate files?