技术知识文章集合TECHNICAL ARCHIVE · 457 DOCUMENTS

显示模式

登录
ARCHIVE DOCUMENTVUE

Vue 3 的 v-if 和 v-for 优先级详解

所属馆藏
Vue
文件格式
Markdown
原始路径
Vue/34-Vue3的v-if 和v-for优先级详解
本文目录8 个章节
  1. 一、Vue 2 中为什么不建议同时使用
  2. 二、Vue 3 的优先级发生了变化
  3. 三、Vue 3 正确的写法
  4. 四、不要把 v-show 当成优先级解决方案
  5. 五、性能和可读性建议
  6. 六、结论
  7. 官方参考
  8. 原文出处

Vue 3 的 v-ifv-for 优先级详解

本文保留原文对 Vue 2、Vue 3 指令优先级差异的说明,并按当前官方文档修正示例。本文中的 Vue 3 以 Vue 3.5 为基线,Vue 2 以 Vue 2.7 的兼容行为为参考。

一、Vue 2 中为什么不建议同时使用

Vue 2 条件渲染官方文档

在 Vue 2 中,如果 v-ifv-for 写在同一个元素上,v-for 的优先级高于 v-if。因此下面的表达式可以访问 itemindex

<template>
  <div class="hello">
    <div
      v-for="(item, index) in list"
      v-if="index === 9"
      :key="item"
    >
      {{ item }}
    </div>
  </div>
</template>

<script>
export default {
  data() {
    return {
      list: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10],
    }
  },
}
</script>

可以把它粗略理解为:先遍历 list,再对每一项判断 v-if。即使最后只渲染一项,每次相关数据更新时也要遍历整个列表:

this.list.map((item, index) => {
  if (index === 9) {
    return item
  }
})

这不一定意味着每次都会产生大量 DOM 操作,但表达式本身确实需要逐项判断,条件越稳定、列表越大,越应该避免把筛选工作放在模板的每一项上。

Vue 2 推荐使用计算属性先过滤

<template>
  <div class="hello">
    <div v-for="item in filteredList" :key="item">
      {{ item }}
    </div>
  </div>
</template>

<script>
export default {
  data() {
    return {
      list: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10],
    }
  },
  computed: {
    filteredList() {
      return this.list.filter((_, index) => index === 9)
    },
  },
}
</script>

计算属性会缓存结果,只有依赖变化时才重新计算;模板只负责遍历已经筛选后的数组。筛选条件如果还依赖用户输入,也可以在计算属性中统一处理。

二、Vue 3 的优先级发生了变化

Vue 3 条件渲染官方文档

Vue 3 迁移指南:v-ifv-for 优先级

在 Vue 3 中,如果两个指令写在同一个元素上,v-if 的优先级高于 v-for。因此下面的代码不能访问 itemindex

<template>
  <div
    v-for="(item, index) in list"
    v-if="index === 9"
    :key="item"
  >
    {{ item }}
  </div>
</template>

v-if 会先求值,但 index 只在 v-for 的作用域中才存在。Vue 3 编译器会报错或运行时提示类似:

Property "index" was accessed during render but is not defined on instance.

所以,Vue 3 的结论不是“既然 v-if 优先级更高就可以鼓励同时使用”,而是更应该避免在同一个元素上同时使用二者。

三、Vue 3 正确的写法

1. 根据每一项决定是否渲染:把 v-for 放到 template

<template>
  <ul>
    <template v-for="(item, index) in list" :key="item.id">
      <li v-if="item.show">
        {{ index }} - {{ item.name }}
      </li>
    </template>
  </ul>
</template>

<script setup>
import { ref } from 'vue'

const list = ref([
  { id: 1, name: '张三', show: false },
  { id: 2, name: '李四', show: true },
])
</script>

v-for 负责建立循环作用域,v-if 写在循环内部的子节点上,因此可以访问 item。Vue 3 还支持把 :key 放在 <template v-for> 上,key 应该稳定且尽量使用业务 id,而不是在列表会增删时使用 index。

如果每次循环实际只渲染一个根节点,也可以直接把 v-for 放在外层容器上:

<ul v-if="list.length">
  <li v-for="item in list" :key="item.id">
    {{ item.name }}
  </li>
</ul>

2. 想在列表为空时跳过整个循环:把 v-if 放到外层

<template>
  <div class="hello">
    <ul v-if="list.length > 0">
      <li v-for="item in list" :key="item.id">
        {{ item.name }}
      </li>
    </ul>
    <p v-else>暂无数据</p>
  </div>
</template>

这里 v-if 控制的是整个列表容器,条件为 false 时连列表的循环也不会执行。这正是“如果想有条件地跳过循环执行,应把 v-if 放到外层”的场景。

3. 需要筛选列表:使用计算属性或普通函数

<script setup>
import { computed, ref } from 'vue'

const list = ref([
  { id: 1, name: '张三', show: false },
  { id: 2, name: '李四', show: true },
])

const visibleList = computed(() => {
  return list.value.filter(item => item.show)
})
</script>

<template>
  <ul>
    <li v-for="item in visibleList" :key="item.id">
      {{ item.name }}
    </li>
  </ul>
</template>

如果筛选逻辑不需要缓存,也可以使用方法;如果只想在模板中格式化当前项,不要为了简单条件再引入复杂抽象。重点是让列表的数据准备和 DOM 结构职责分离。

四、不要把 v-show 当成优先级解决方案

v-ifv-show 都可以控制显示,但作用不同:

  • v-if 是真正的条件渲染,条件为 false 时节点和子组件不会创建;切换时会销毁/重新创建对应分支。
  • v-show 始终渲染节点,只切换 CSS 的 display;适合频繁切换、但初次渲染成本可以接受的场景。
  • v-show 不能与 v-elsev-else-if 配合,也不能解决 v-if/v-for 的作用域优先级问题。

例如:

<button @click="open = !open">切换</button>
<div v-show="open">频繁显示和隐藏的内容</div>

五、性能和可读性建议

  1. 不要在同一个元素上同时使用 v-ifv-for
  2. 对列表项筛选使用计算属性,把过滤结果直接交给 v-for
  3. 控制整个列表是否存在时,把 v-if 放到列表容器上。
  4. 依据单项属性判断时,使用 <template v-for> 包裹 v-if 子节点。
  5. 列表增删和排序时使用稳定的业务 key,不要无条件使用 index。
  6. 只有需要频繁切换且希望保留 DOM 时才考虑 v-show
  7. 计算属性和过滤函数本身也有成本;数据量特别大时,还应考虑分页、虚拟列表和服务端筛选。

六、结论

  • 在 Vue 2 中,同一元素上的 v-for 优先级高于 v-if
  • 在 Vue 3 中,同一元素上的 v-if 优先级高于 v-for,循环别名不能在 v-if 中使用。
  • Vue 2、Vue 3 都不推荐在同一元素上混用二者。
  • 按单项筛选时使用 <template v-for>;按整体条件控制时把 v-if 放在外层;复杂筛选使用 computed。

官方参考

原文出处

作者:孤山渐青

原文链接:https://juejin.cn/post/7128328827981266980

来源:稀土掘金。著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处。

457 DOCUMENTS · 10 COLLECTIONS
ARCHIVE SEARCH457 篇文章

SEARCH GUIDE

输入关键词开始搜索

支持搜索文章标题、所属分类和原始文档路径。

按分类浏览

10 COLLECTIONS