v-if 与 v-for 的优先级对比

目录

vue2

vue3

总结


先说结论

  1. 在 Vue2 中,v-for 的优先级高于 v-if
  2. 在 Vue3 中,v-for 的优先级高于 v-for

注意:Vue2 和 Vue3 中都不推荐同时使用 v-if 和 v-for

vue2

当它们处于同一节点,v-for 的优先级比 v-if 更高,这意味着 v-if 将分别重复运行于每个 v-for 循环中。当你只想为部分项渲染节点时,这种优先级的机制会十分有用,如下:

复制代码
<li v-for="todo in todos" v-if="!todo.isComplete">
  {{ todo }}
</li>

上面的代码将只渲染未完成的 todo。

而如果你的目的是有条件地跳过循环的执行,那么可以将 v-if 置于外层元素 (或 <template>) 上。如:

复制代码
<ul v-if="todos.length">
  <li v-for="todo in todos">
    {{ todo }}
  </li>
</ul>
<p v-else>No todos left!</p>

一般我们在两种常见的情况下会倾向于这样做:

  • 为了过滤一个列表中的项目 (比如 v-for="user in users" v-if="user.isActive")。在这种情形下,请将 users 替换为一个计算属性 (比如 activeUsers),让其返回过滤后的列表。

  • 为了避免渲染本应该被隐藏的列表 (比如 v-for="user in users" v-if="shouldShowUsers")。这种情形下,请将 v-if 移动至容器元素上 (比如 ulol)。

当 Vue 处理指令时,v-forv-if 具有更高的优先级,所以这个模板:

复制代码
<ul>
  <li
    v-for="user in users"
    v-if="user.isActive"
    :key="user.id"
  >
    {{ user.name }}
  </li>
</ul>

将会经过如下运算:

复制代码
this.users.map(function (user) {
  if (user.isActive) {
    return user.name
  }
})

因此哪怕我们只渲染出一小部分用户的元素,也得在每次重渲染的时候遍历整个列表,不论活跃用户是否发生了变化。

通过将其更换为在如下的一个计算属性上遍历:

复制代码
computed: {
  activeUsers: function () {
    return this.users.filter(function (user) {
      return user.isActive
    })
  }
}

<ul>
  <li
    v-for="user in activeUsers"
    :key="user.id"
  >
    {{ user.name }}
  </li>
</ul>

我们将会获得如下好处:

  • 过滤后的列表 会在 users 数组发生相关变化时才被重新运算,过滤更高效。
  • 使用 v-for="user in activeUsers" 之后,我们在渲染的时候遍历活跃用户,渲染更高效。
  • 解耦渲染层的逻辑,可维护性 (对逻辑的更改和扩展) 更强。

为了获得同样的好处,我们也可以把:

复制代码
<ul>
  <li
    v-for="user in users"
    v-if="shouldShowUsers"
    :key="user.id"
  >
    {{ user.name }}
  </li>
</ul>

更新为:

复制代码
<ul v-if="shouldShowUsers">
  <li
    v-for="user in users"
    :key="user.id"
  >
    {{ user.name }}
  </li>
</ul>

通过将 v-if 移动到容器元素,我们不会再对列表中的每个 用户检查 shouldShowUsers。取而代之的是,我们只检查它一次,且不会在 shouldShowUsers 为否的时候运算 v-for

vue3

当它们同时存在于一个节点上时,v-ifv-for 的优先级更高。这意味着 v-if 的条件将无法访问到 v-for 作用域内定义的变量别名:

复制代码
<!--
 这会抛出一个错误,因为属性 todo 此时
 没有在该实例上定义
-->
<li v-for="todo in todos" v-if="!todo.isComplete">
  {{ todo.name }}
</li>

在外先包装一层 <template> 再在其上使用 v-for 可以解决这个问题 (这也更加明显易读)

复制代码
<template v-for="todo in todos">
  <li v-if="!todo.isComplete">
    {{ todo.name }}
  </li>
</template>

注意

同时使用 v-ifv-for不推荐的,因为这样二者的优先级不明显。

两种常见的情况可能导致这种用法:

  • 过滤列表中的项目 (例如,v-for="user in users" v-if="user.isActive")。在这种情况下,可以用一个新的计算属性来替换 users,该属性返回过滤后的列表 (例如 activeUsers)。

  • 避免渲染应该隐藏的列表 (例如 v-for="user in users" v-if="shouldShowUsers")。在这种情况下,将 v-if 移至容器元素 (如 ulol)。


有时,我们希望显示数组经过过滤或排序后的内容,而不实际变更或重置原始数据。在这种情况下,你可以创建返回已过滤或已排序数组的计算属性。

复制代码
const numbers = ref([1, 2, 3, 4, 5])

const evenNumbers = computed(() => {
  return numbers.value.filter((n) => n % 2 === 0)
})

<li v-for="n in evenNumbers">{{ n }}</li>

在计算属性不可行的情况下 (例如在多层嵌套的 v-for 循环中),你可以使用以下方法:

复制代码
const sets = ref([
  [1, 2, 3, 4, 5],
  [6, 7, 8, 9, 10]
])

function even(numbers) {
  return numbers.filter((number) => number % 2 === 0)
}

<ul v-for="numbers in sets">
  <li v-for="n in even(numbers)">{{ n }}</li>
</ul>

在计算属性中使用 reverse()sort() 的时候务必小心!这两个方法将变更原始数组,计算函数中不应该这么做。请在调用这些方法之前创建一个原数组的副本:

复制代码
- return numbers.reverse()
+ return [...numbers].reverse()

总结

Vue.js 中使用最多的两个指令就是 v-ifv-for,因此开发者们可能会想要同时使用它们。虽然不建议这样做,但有时确实是必须的,于是我们想提供有关其工作方式的指南。

由于语法上存在歧义,建议避免在同一元素上同时使用两者。

比起在模板层面管理相关逻辑,更好的办法是通过创建计算属性筛选出列表,并以此创建可见元素。

2.x 版本中在一个元素上同时使用 v-ifv-for 时,v-for 会优先作用。

3.x 版本中 v-if 总是优先于 v-for 生效。

相关推荐
仰望星空_Star10 分钟前
Java证书操作
java·开发语言
女王大人万岁11 分钟前
Go语言time库核心用法与实战避坑
服务器·开发语言·后端·golang
云游云记12 分钟前
php Token 主流实现方案详解
开发语言·php·token
m0_7482299912 分钟前
Laravel5.x核心特性全解析
开发语言·php
河北小博博12 分钟前
分布式系统稳定性基石:熔断与限流的深度解析(附Python实战)
java·开发语言·python
岳轩子13 分钟前
JVM Java 类加载机制与 ClassLoader 核心知识全总结 第二节
java·开发语言·jvm
Yolanda9424 分钟前
【项目经验】钉钉免密登录实现
前端·javascript·钉钉
2601_9496130226 分钟前
flutter_for_openharmony家庭药箱管理app实战+药品详情实现
java·前端·flutter
摘星编程30 分钟前
在OpenHarmony上用React Native:collapsable节点优化策略
javascript·react native·react.js
智航GIS35 分钟前
ArcGIS Python零基础脚本开发教程---1.1 Describe 函数
开发语言·python·arcgis