在vue2和vue3的官方文档里都写到不推荐 v-if和v-for同时使用,如下代码所示:
javascript
<li v-for="todo in todos" v-if="!todo.isComplete">
{{ todo.text }}
</li>
一、vue3文档:列表渲染 | Vue.js
在vue3中,是因为当它们同时存在于一个节点上时,v-if
比 v-for
的优先级更高。这意味着 v-if
的条件将无法访问到 v-for
作用域内定义的变量别名。 v-if里是无法访问到todo的,这将会报错。
二、vue2文档:列表渲染 --- Vue.js
在vue2中,v-for 的优先级比 v-if 更高,也就是说在v-if中可以访问到v-for作用域内定义的变量别名 ,因此不会跟vue3一样报错,但并不推荐这么做,原因如下:
- 性能问题 :将
v-for
和v-if
放在同一个元素上会导致性能下降。Vue 必须为每一个在v-for
中的项目都检查v-if
的条件,这会增加不必要的计算量。特别是当todos
数组很大时,这种性能问题会更加明显。详见文章末尾的附录。 - 逻辑可读性 :从逻辑和可读性的角度来看,将过滤逻辑(
v-if
)和渲染逻辑(v-for
)混合在一起可能会导致代码难以理解和维护。最好是先过滤数据,然后再进行渲染。
推荐写法:
写法一、先过滤数据,再使用v-for
javascript
<template>
<ul>
<li v-for="todo in filteredTodos" :key="todo.id">
{{ todo.text }}
</li>
</ul>
</template>
<script>
export default {
data() {
return {
todos: [
{ id: 1, text: 'Learn Vue', isComplete: false },
{ id: 2, text: 'Build something awesome', isComplete: true },
// ... more todos
]
}
},
computed: {
// 得到过滤后的数据
filteredTodos() {
return this.todos.filter(todo => !todo.isComplete);
}
}
}
</script>
写法 二:利用<template>元素,将 v-if
放在 v-for
的子元素中,而不是与 v-for
直接放在同一个元素上。(在vue2中,不推荐使用,逻辑可读性虽然没问题,但数据多时,还是可能存在性能问题;在 Vue 3 中,编译器能够识别 v-for
在 v-if
的子元素上的使用情况,并进行优化,以避免不必要的虚拟 DOM 节点的创建,可以使用该写法)
javascript
<template>
<ul>
<template v-for="todo in todos" :key="todo.id">
<li v-if="!todo.isComplete">
{{ todo.text}}
</li>
</template>
</ul>
</template>
<script>
export default {
data() {
return {
todos: [
{ id: 1, text: 'Learn Vue', isComplete: false },
{ id: 2, text: 'Build something awesome', isComplete: true },
// ... more todos
]
}
}
}
</script>
附录:
在 Vue 2 中,当你同时在一个元素上使用 v-for
和 v-if
,Vue 的渲染逻辑实际上是这样的:
-
v-for 优先于 v-if :首先,
v-for
会为todos
数组中的每个项目创建 DOM 节点。这意味着,它会为数组中的每个todo
项目都创建一个<li>
元素。 -
v-if 随后应用 :然后,Vue 会检查每个由
v-for
创建的<li>
元素上的v-if
条件。如果v-if
的条件不满足(即todo.isComplete
为true
),则该节点不会被渲染到 DOM 中。但是,这并不意味着节点被销毁,而是它们只是简单地不被添加到 DOM 树中。 -
节点的销毁与复用 :如果
todos
数组发生变化(例如,项目被添加、删除或更改),Vue 会重新计算v-for
和v-if
的结果,并相应地更新 DOM。这意味着,即使v-if
条件不满足,由v-for
创建的节点可能仍然存在在 Vue 的虚拟 DOM 中,只是它们不会出现在实际的 DOM 树中。这些节点可以被 Vue 复用,如果它们在未来再次满足v-if
的条件。 -
性能影响 :即使某些节点因为
v-if
条件不满足而不会被渲染到 DOM 中,它们仍然会被 Vue 创建和追踪,这可能会对性能产生影响,尤其是当todos
数组很大时。 -
最佳实践 :为了获得最佳性能,你应该避免在同一元素上同时使用
v-for
和v-if
。如果可能,应该使用计算属性或方法来预先过滤数据,然后只对过滤后的结果进行v-for
渲染。这样做可以减少不必要的节点创建和销毁,提高应用程序的性能。
因此,对于你给出的代码示例,即使 todo.isComplete
为 true
,由 v-for
创建的 <li>
节点仍然会被 Vue 创建和追踪,只是它们不会出现在最终的渲染结果中。为了优化性能,你应该将过滤逻辑移至计算属性或方法中,并在 v-for
中仅渲染过滤后的数据。