Vue Directive Execution Order: v-if Versus v-for
In Vue.js, v-if manages conditional rendering based on expression truthiness. Conversely, v-for iterates over data sources to generate multiple DOM elements. Optimizing list rendering requires assigning a unique key property to each item, enabling the Diff algorithm to track node identity efficiently.
Compilation Behavior and Precedence
Both directives are processed during the template-to-render-function phase. When placed on the same element, execution order matters significantly for performance.
Consider an implementation where both directives coexist on a single tag:
<div id="root">
<p v-if="showTasks" v-for="task in taskList">
{{ task.name }}
</p>
</div>
With associated reactive state:
new Vue({
el: '#root',
data() {
return {
showTasks: true,
taskList: [
{ name: 'Alpha' },
{ name: 'Beta' }
]
}
}
})
Inspecting the compiled render function reveals that v-for (represented internally as _l) processes before the conditional check (.render). The logic effectively becomes an iteration containing an inner condition:
function anonymous() {
with (this) {
return _c('div', [
_l((taskList), (t) =>
(showTasks) ? _c('p', [t.name]) : _e()
)
])
}
}
This structure confirms that the loop executes first, followed by the condition evaluation for every item, which is inefficient.
Alternative Structure
To prioritize condition checking, apply v-if to a wrapper container instead of the list item:
<template v-if="showTasks">
<li v-for="task in taskList" :key="task.id">{{ task.name }}</li>
</template>
The resulting compilation logic shifts the conditional checkoutside the iteration scope:
function anonymous() {
with (this) {
return (showTasks) ? (
_l((taskList), (t) => _c('li', [t.name]))
) : _e()
}
}
Source analysis indicates that directive processing checks for attributes before if attributes within the generation pipeline.
Optimization Strategies
- Separate Directives: Never bind
v-ifandv-fordirectly to the same element. This forces a full iteration regardless of visibility state. - Wrapper Tag Usage: Utilize
<template>or another structural parent for conditional wrapping. The template does not render physical DOM nodes but acts as a logical block for instructions. - Computed Filtering: If filtering depends on list properties, manipulate the data source via computed properties rather than template directives:
computed: {
filteredTasks() {
return this.allData.filter(t => t.isVisible === true)
}
}
Then use the computed result in the loop:
<li v-for="task in filteredTasks" :key="task.id">
{{ task.name }}
</li>