vue.js 的 v-if 在编译阶段转化为带条件分支的渲染函数,通过响应式依赖追踪实现按需渲染,而非运行时 dom 操作;要求 v-if/v-else-if/v-else 相邻,与 v-for 共存时优先展开循环再过滤,推荐用计算属性预处理以提升性能。

Vue.js 编译器对 v-if 的处理不是运行时“实时判断 + DOM 操作”,而是在编译阶段就将条件逻辑转化为可执行的渲染函数,最终在响应式更新时高效触发。它不依赖 JavaScript 的 if 语句字面量,而是通过生成带条件分支的虚拟节点(VNode)结构来实现真正的按需渲染。
v-if 的编译本质:条件表达式转 VNode 分支
当编译器遇到 <div v-if="show">Hello</div>,会将其转换为类似这样的渲染函数逻辑:
() => show.value ? h('div', 'Hello') : null
-
show.value是响应式引用,其变化会触发整个渲染函数重新执行; - 返回
null表示该节点不参与本次渲染,不会生成任何真实 DOM; - 若为
true,则调用h()创建完整 VNode,并进入挂载/更新流程。
这个过程发生在编译期静态分析 + 运行时动态求值结合阶段:模板语法被提前解析成 AST,再生成带条件判断的 JS 渲染函数,而非每次 diff 都去解析字符串。
条件链(v-if / v-else-if / v-else)的编译约束
编译器要求这些指令必须严格相邻、无间隔,否则报错:
<div v-if="a"></div> <span></span><div v-else></div>❌ 不合法,中间插入了其他元素;<div v-if="a"></div> <div v-else></div>✅ 合法,构成原子化条件块;-
编译器会把整组指令合并为一个三元或嵌套条件表达式,例如:
() => a.value ? h('div', 'A') : b.value ? h('div', 'B') : h('div', 'Else')
这种扁平化结构让 Vue 能在一次 patch 中完成整个分支判定,避免多次 DOM 插入/移除。
使用 JSON Schema 验证 JSON 数据,从示例 JSON 生成 schema,并将其转换为 TypeScript 接口、Python 数据类或 Markdown 文档。
与 v-for 共存时的编译优先级
当 v-if 和 v-for 出现在同一标签上(如 <li v-for="x in list" v-if="x.active">),编译器强制先展开 v-for,再对每个生成项应用 v-if:
- 先遍历
list,生成 N 个<li>的 VNode; - 对每个
<li>单独计算x.active,决定是否保留该节点; - 等价于:
list.filter(x => x.active).map(x => h('li', x.text));
⚠️ 注意:这可能导致性能浪费(尤其 list 很大但 active 很少),推荐改写为计算属性预过滤:
const activeItems = computed(() => list.filter(x => x.active))
然后只用 v-for="x in activeItems",让过滤逻辑脱离模板编译路径。
动态条件的响应式联动机制
v-if 所依赖的表达式,只要包含响应式数据(ref、reactive、computed),就会自动建立依赖追踪:
-
v-if="user.role === 'admin' && permissions.has('edit')"
→ 编译后会收集user.role和permissions.has的响应式依赖; - 任一依赖变更,都会触发父组件的
render函数重执行; - 整个条件块重新求值,VNode 树局部重建,DOM 随之同步。
这种机制保证了“数据驱动视图”的一致性,也意味着:不要在 v-if 中调用有副作用的函数(如 api.fetch()),因为它可能被反复执行。
不复杂但容易忽略
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!










