VueJS: <template> vs <div> or related for grouping elements for conditional rendering - vue.js

In Vue.js, if you want to conditionally render multiple elements via a v-if/v-else-if directive you can wrap them inside <template> tags and apply the directive to the <template> tag, as explained here. However, you can also do the same with <div> tags wrapping the multiple elements. Are there any noticeable performance benefits to using <template> over <div> or any other similar native HTML5 tag?

I doubt there is a performance change but a big difference is that the <template> node does not appear in the DOM.
This is an important distinction especially when grouping children that are the only valid node types for their parent such as list items, table rows, etc:
This is valid:
<ul>
<template v-if="something">
<li>Text</li>
<li>Text</li>
</template>
</ul>
This is not:
<ul>
<div v-if="something">
<li>Text</li>
<li>Text</li>
</div>
</ul>

I know that the question is quite old, but I found out one more thing
if you use divs - your div will be in DOM, but empty, if v-if is false and it can make some spaces looks like margins
if you use template - you don't have anything in DOM and it just don't appear

Related

Why are Vue dynamic components destroyed inside loop every re-render?

I run into an issue with Vuejs 2.x version (latest). While rendering a list of item inside a loop, if I make changes to the items then the normal components are not destroyed but the dynamic components will always be destroyed:
I have put a short sample code here:
https://gist.github.com/yellow1912/fc1c053e07c1ca136148484cf7f79d1a
I have also put a codepen here:
https://codepen.io/raineng/pen/zYGOXYY?editors=1111
<nl-test inline-template>
<div>
<div v-on:click="increase"> increase here please </div><br><br>
<div v-on:click="decrease"> decrease here please </div>
<ul>
<li v-for="(value, key) in getItems()" :key="key">
printing
<component :is="getItem()" :key="key"></component>
<nl-test inline-template>
<div>
this is a test here
</div>
</nl-test>
</li>
</ul>
</div>
</nl-test>
To see what I mean, open the console tab on codepen, click the add item and you will see that the dynamic component items are destroyed and re-created everytime.
I found out why, I need to use keep-alive:
https://v2.vuejs.org/v2/guide/components-dynamic-async.html
To quote:
When switching between these components though, you’ll sometimes want
to maintain their state or avoid re-rendering for performance reasons
Recreating dynamic components is normally useful behavior, but in this
case, we’d really like those tab component instances to be cached once
they’re created for the first time. To solve this problem, we can wrap
our dynamic component with a element
Wasted 2 days on this issue and then I found the answer just a moment after posting this on StackOverflow. Hope it helps someone.

Does Vuejs allow to use nested template tag?

Is it allowed to use <template> tag inside template something like this?
I have to check some value.
<template>
<div>
<template v-for="category_field in category_fields">
<template v-if="category_field.show_type == 'new-row'">
//and also here can be more nested template tags
</template>
<template v-else>
//and also here can be more nested template tags
</template>
</template>
</div>
</template>
I am using this system in my project and wondering whether this is correct.
Yes, you can nest <template>s, and it's quite useful sometimes.
Vue's <template> is very similar to <React.Fragment>. It's a virtual container used for grouping or applying layout logic (using structural directives - e.g: v-for, v-if), without creating an actual DOM element.
Because it doesn't output a DOM element, it's used as a wrapper for SFC's HTML markup.
Technically, the limit on having only one child in Vue 2 was not coming from the <template> tag itself, but from the components, as Vue required them to have only one root element, which became the component's $el. More detail here.
Besides the typical usage of wrapping an SFC's markup, <template> tags are also used for:
combining structural directives (v-for, v-if) and letting Vue know in which order to apply the directives, as changing the order would likely change the result
applying layout or rendering logic (e.g: v-if) to multiple elements at once, without having to create an actual DOM wrapper around them, particularly useful when you don't want to break the parent/child relation of DOM elements (e.g: flex or grid parent/children, <ul>/<ol> + <li>, <tr> + <td>, <tbody> + <tr>, etc.).
reducing template boilerplate (e.g: moving the same v-if from multiple siblings on a <template> wrapper, so the condition is only written once).
Try using this. <template> require only one child.
<template v-for="category_field in category_fields">
<div>
<template v-if="category_field.show_type == 'new-row'">
//and also here can be more nested template tags
</template>
<template v-else>
//and also here can be more nested template tags
</template>
</div>
</template>

We also need to provide each component with a "key" vuejs

<div id="app-7">
<ol>
<!--
Now we provide each todo-item with the todo object
it's representing, so that its content can be dynamic.
We also need to provide each component with a "key"
-->
<todo-item
v-for="item in groceryList"
v-bind:todo="item"
v-bind:key="item.id">
</todo-item>
</ol>
</div>
My question why We also need to provide each component with a "key" where any example that can help me to understand please
Vue uses the key attribute to "track" each node rendered by a v-for loop, for performance purpose.
You can find more information here https://v2.vuejs.org/v2/guide/list.html#key
When Vue is updating a list of elements rendered with v-for, by default it uses an “in-place patch” strategy. If the order of the data items has changed, instead of moving the DOM elements to match the order of the items, Vue will patch each element in-place and make sure it reflects what should be rendered at that particular index. This is similar to the behavior of track-by="$index" in Vue 1.x.
that's from the docs

Oddity with templates and root node with vue.js

I think this is possibly a bug I've stumbled across, not sure. I'm getting this Vue.js warning for a component:
vue.js:2611 [Vue warn]: Cannot use <template> as component root element because it may contain multiple nodes:
The problem seems to be this:
<template id="tpl-field">
<template v-if="fieldType==='checkbox-inline'">
<label class="checkbox-inline">[SNIP]</label>
</template>
<template v-else>
[SNIP]
</template>
</template>
So I have two template nodes, which seems to be the multiple nodes it's choking on (certainly each of the template nodes contains just a single node). Yet this is an if-else in Vue - if one of the nodes is present, the other logically cannot be.
Demo of the problem here: https://jsfiddle.net/jonmor51/Ldz3k0jp/1/. If I wrap everything in a div, it works. But without, it fails. (Unfortunately, in the context where I want to use this, namely for inline checkboxes within a Bootstrap grid, wrapping in a div breaks things).
Not sure if this will solve your problem with bootstrap... but you could wrap you inner templates with a <transition> tag and set a key to each one.
Please check this working fiddle
https://jsfiddle.net/AldoRomo88/7c7znu3p/
helpful thing - just use div display: contents as a root of the component and browser will ignore that element and consider child elements (which can be many) as children of upper dom element
<div style="display: contents">
<template v-if="...">
<template v-for="..."> ...
</template>
<template v-if="...">
</template>
</div
works even inside tables!
The inner templates direct children, are they single elements? If so, you can just remove the inner templates and move v-if to the label.
Or, just use span rather than div as your quick fix, which won't break inline elements' style.

Aurelia: if binding and content selectors

I have found content selectors don't seem to work when an if binding is used on a parent element. For example:
some-element.html
<template>
<div if.bind="true">
This will appear
<content></content>
</div>
</template>
app.html
<template>
<some-element>This will not appear</some-element>
</template>
This works fine if I don't use the if binding, but will not render <content> when I do use the if binding. Is there something I'm doing wrong here or is there a way to get this to work as expected?
I'll be adding this to our documentation soon, but for now, here is #EisenbergEffect's explanation from https://github.com/aurelia/framework/issues/375
"No. That is a characteristic of the shadow dom. The content selection points have to be static. They cannot be added ore removed dynamically. If you want to hid them, then consider using show.bind instead."