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

显示模式

登录
ARCHIVE DOCUMENTVUE

v-for比v-if优先级更高?

所属馆藏
Vue
文件格式
Markdown
原始路径
Vue/06-v-for比v-if优先级更高?
本文目录11 个章节
  1. 自我介绍
  2. 前言
  3. 为什么要看编译结果
  4. Vue 2:v-for 先处理
  5. Vue 3:v-if 先处理
  6. 两种必须掌握的正确写法
  7. 补充:控制整个列表是否显示
  8. 选择对照
  9. 总结
  10. 加餐:编译过程
  11. 官方参考

v-for比v-if优先级更高?

Category(分类): Vue Status: 已校订(Vue 2 / Vue 3 对照)

自我介绍

大家好,我叫 JetTsang,之前都是在掘金潜水,现在偶尔做一些内容输出。

前言

v-ifv-for 哪个优先级更高?这个问题必须先限定 Vue 版本,而且这里的“优先级”指的是同一元素上结构指令的编译顺序与作用域规则,不是 JavaScript 运算符优先级。

<!-- 不推荐:同一个元素同时使用 v-for 和 v-if -->
<div
  v-for="item in itemList"
  v-if="item.id === 1"
  :key="item.id"
>
  {{ item.name }}
</div>

核心结论:

  • Vue 2:同元素上 v-for 优先于 v-if,所以 v-if 能访问循环别名 item,但会先遍历再逐项判断。
  • Vue 3:同元素上 v-if 优先于 v-for,所以 v-if 条件中访问不到循环别名 item
  • Vue 2 和 Vue 3 都不推荐把两者写在同一元素上。 应根据意图改写模板。

为什么要看编译结果

v-if 是条件渲染,v-for 是列表渲染,二者都是 Vue 模板语法,需要经过编译,再由 render function 生成 VNode 并 patch 到 DOM。

原文配图 06-01

可以使用对应版本的 Template Explorer 辅助观察:

具体 render 输出会随编译器版本和选项变化,不能把某张截图的代码形态当作稳定公共 API;稳定结论应以官方文档和对应版本源码为准。

示例数据:

const list = [
  { name: 'JetTsang', id: 1, active: true },
  { name: 'juejin', id: 2, active: false },
  { name: 'Vue', id: 3, active: true },
]

Vue 2:v-for 先处理

Vue 2 编译同节点指令时,先生成列表迭代,再在每次迭代里判断条件。原作者把模板放进 Vue 2 Template Explorer,得到如下 render 结果:

原作者输入 Vue 2 示例模板(06-02)

原作者观察 Vue 2 render 结果(06-03)

这段历史输出里的 helper 可这样读:

  • _c() 是编译器生成代码使用的 createElement helper,用来创建 VNode;它是 Vue 2 内部 render helper 形态,不是建议业务代码依赖的稳定 API。
  • _l(list, callback)renderList helper:遍历 list,把当前 item 传给回调。
  • 条件表达式位于 _l 的循环回调中,因此先建立 item 作用域,再逐项计算 item.id === 1
  • 未命中条件时,生成 _e() 对应的空/注释 VNode;原作者也展示了注释节点的实际形态:

Vue 2 未命中 v-if 时的注释节点(06-04)

因此在 Vue 2 中,这段代码的 item 有效,但每个列表项都要执行条件判断。如果目的是过滤列表,官方建议先用计算属性得到过滤结果;原作者截图中的开发提示也是这一上下文:

原作者记录的 Vue 2 过滤列表提示(06-05)

需要强调:即使 Vue 2 允许别名这样使用,同节点合用仍是官方风格指南中应避免的写法。

Vue 3:v-if 先处理

Vue 3 中,同节点 v-if 的优先级高于 v-for。这意味着编译 v-if="item.id === 1" 时,v-for 尚未建立 item 的循环作用域;item 会被当成组件实例上下文属性,通常得到“属性未定义”警告或运行时错误。

原作者输入 Vue 3 示例模板(06-06)

原作者观察 Vue 3 编译结果(06-07)

原文把变化描述为“把 v-if 提升到循环外以节省计算”,容易造成误解:它并不是让原来的同节点写法在 Vue 3 中合法并自动获得性能优化。原写法访问不到 item,应直接重写。这个变化也不能武断归因于单一性能原因,它首先是 Vue 3 明确的编译顺序和作用域语义。

以下两图明确是原作者当时的比较和推导过程:作者尝试把 v-if 放到外层 <template>,再把 v-for 包在其中,比较生成的 render 形态,并据此推导 Vue 3 是“单纯性能优化”。截图保留其历史上下文,但这个归因不成立:改写后的条件若依赖 item 仍没有循环作用域,而且不同模板语义不能只凭输出外形相近视为等价。

原作者尝试用外层 template 理解提升(06-08)

原作者比较 template 编译结果(06-09,性能归因不成立)

两种必须掌握的正确写法

写法一:计算属性预过滤

当需求是“只展示符合条件的项”时,先用 computed 过滤。过滤逻辑可复用、可测试,模板也更清楚。

<script setup lang="ts">
import { computed, ref } from 'vue'

interface User {
  id: number
  name: string
  active: boolean
}

const users = ref<User[]>([
  { id: 1, name: 'JetTsang', active: true },
  { id: 2, name: 'juejin', active: false },
  { id: 3, name: 'Vue', active: true },
])

const activeUsers = computed(() =>
  users.value.filter((user) => user.active),
)
</script>

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

Vue 2 Options API 对应写法:

computed: {
  activeUsers() {
    return this.users.filter((user) => user.active)
  },
}

写法二:外层 <template v-for>,内层元素 v-if

如果条件确实必须逐项判断,并且需要访问循环别名,先由 <template v-for> 建立作用域,再在内部元素上判断:

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

Vue 3 中 key 应放在 <template v-for>,而不是放到内部 li。这正是任务要求的逐项条件写法。

如果维护 Vue 2,<template> 不能充当带 key 的真实元素,通常要把 key 放到实际子元素:

<!-- Vue 2 历史写法 -->
<template v-for="user in users">
  <li v-if="user.active" :key="user.id">
    {{ user.name }}
  </li>
</template>

补充:控制整个列表是否显示

若条件与单个 item 无关,例如“用户有权限时才显示整组列表”,将 v-if 放在容器上,避免每项重复判断:

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

这与“过滤 item”是不同意图,不必一律使用 computed。

选择对照

需求推荐写法
只展示符合条件的项computed 预过滤后 v-for
整个列表显示/隐藏容器 v-if,子元素 v-for
必须逐项判断且访问别名Vue 3:<template v-for :key> + 内层元素 v-if
同元素同时写 v-forv-ifVue 2/3 都应避免

总结

Vue 2 同元素上 v-for 优先,Vue 3 同元素上 v-if 优先;Vue 3 的条件因而访问不到 v-for 别名。不要把这个变化简单解释成一次“性能提升”,也不要继续依赖同节点合用。按项过滤使用 computed;条件必须逐项判断时,在 Vue 3 使用外层 <template v-for :key>、内层元素 v-if

加餐:编译过程

从概念上看,Vue 模板编译会经历:

  1. parse:把模板字符串解析为 AST;
  2. transform / optimize:转换结构指令并分析可优化信息;
  3. generate:生成 render function 代码。

不同 Vue 大版本的编译器内部阶段和输出形式并不完全相同,学习源码时应固定版本。

官方参考

作者:JetTsang 原文链接:https://juejin.cn/post/7217810344696594488 来源:稀土掘金。著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处。

457 DOCUMENTS · 10 COLLECTIONS
ARCHIVE SEARCH457 篇文章

SEARCH GUIDE

输入关键词开始搜索

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

按分类浏览

10 COLLECTIONS