Vue.js 之 v-if 与 v-for 之争

同一个标签上同时写 v-ifv-forVue 2 是循环先跑,Vue 3 是判断先跑。顺序一反,旧代码要么变慢、要么直接报错。

Vue 2 Vue 3
谁优先 v-for 优先 v-if 优先
实际顺序 先遍历,再逐个判断 先判断条件,再考虑循环
旧写法后果 浪费(遍历全部再过滤) 报错(v-if 拿不到循环变量)

别急着记规则。先想清楚:一个标签上同时塞两个指令,到底是在让谁先干活


用一条流水线想明白

v-for 想成一条生产流水线,把 v-if 想成流水线上的质检员。

  • Vue 2:流水线先开工,零件一个个造出来,质检员站在末尾,逐个挑出不合格品。能干活,但每一个零件都做了——哪怕最后扔掉 999 个。
  • Vue 3:质检员被挪到了流水线开工之前。他的规则是"只放行合格零件",可零件还没生产出来,他眼前空空如也。系统直接报错:你让我检查一个还不存在的东西?

这就是优先级反转的全部实质。不是语法变复杂了,是"先有零件还是先有判断"这件事的逻辑变了。


具体的坑在哪

最常见的写法,是用 v-if 来"过滤列表":

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

在 Vue 2 里,上面这段代码能跑,但藏着两个问题:

  1. 浪费。先遍历全部 users,每生成一个 <li> 才判断 isActive。1000 个用户只有 1 个活跃,也得走 1000 次循环。
  2. 意图落空。如果你想写"列表为空就不渲染",v-if="users.length" 写在 v-for 同元素上根本无效——v-for 先跑,空数组直接循环 0 次,v-if 形同虚设。

在 Vue 3 里,同样这段代码直接报编译警告或运行时错误:v-if 先执行时,user 还不存在(循环还没开始),user.isActive 等于 undefined.isActive。Vue 不让你这么写了。


怎么写才对

Vue 3 推荐两招:

第一招,计算属性先把清单准备好(流水线开工前,合格零件清单就已经交到手上):

const activeUsers = computed(() =>
  users.value.filter(u => u.isActive)
)
<li v-for="user in activeUsers" :key="user.id">
  {{ user.name }}
</li>

第二招,<template> 包一层 v-for,把 v-if 放到内层(质检员请回流水线末尾):

<template v-for="user in users" :key="user.id">
  <li v-if="user.isActive">{{ user.name }}</li>
</template>

注意:这一招只是"语法正确的逐条过滤",它不会减少遍历次数——源数组照样过一遍,渲染时才逐条判 v-if。纯过滤场景下,上面第一招(computed)才是真正更省的:它在 JS 层先把数组变短,v-for 只遍历剩下的几项,而且结果带缓存、只在依赖变化时重算。所以"过滤"优先用 computed;<template> 写法适合「每个 item 需要不同的条件渲染逻辑」这种不是单纯过滤的情况。

Vue 2 想要"整体条件渲染",v-if 提到父元素,别和 v-for 挤一起:

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

为什么要改

核心动机就一句话:同一个元素上同时用两个指令,几乎从来不是好主意。

Vue 2 的"循环优先"会让很多人无意中写出"遍历全部再过滤"的低效代码,而且不易察觉——它不报错,只是慢。Vue 3 改成"判断优先 + 访问不到循环变量",等于用报错把这种写法挡在门外,逼你改用 computed 或分层,写出更省、更清晰的代码。

这不是故意为难你。是把"正确的写法"变成了"唯一能跑的写法"。

©著作权归作者所有,转载或内容合作请联系作者
【社区内容提示】社区部分内容疑似由AI辅助生成,浏览时请结合常识与多方信息审慎甄别。
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

友情链接更多精彩内容