上周上线一个订单管理后台,凌晨3点被电话叫醒------页面卡死,CPU直接拉满。打开监控一看,一个表格渲染了2000多条数据,每条数据都带着5个嵌套的v-if条件判断。这场景熟悉吗?今天咱们就聊聊这个看似简单却暗藏杀机的组合:v-if和v-for的混用。
从血泪案例说起:为什么我的页面崩了?
来看这段真实业务代码(已脱敏):
vue
<template>
<div>
<!-- 错误写法:v-for和v-if直接混用 -->
<div v-for="item in list" v-if="showItem(item)" :key="item.id">
{{ item.content }}
</div>
</div>
</template>
当list有2000条数据,showItem需要进行复杂计算时,你会看到:
- 无谓计算 :即使最终只渲染50条数据,
showItem()仍会被执行2000次 - 重复渲染:Vue会在每次数据变化时重新遍历整个列表
- 内存泄漏:某些情况下会产生未清理的虚拟DOM节点
测试数据说话:在2000条数据的场景下,纯v-for耗时30ms,而混用v-if后飙升至450ms------15倍的性能差距。
背后原理:编译器到底做了什么?
你以为Vue会智能地优化这种写法?Too young。来看编译后的渲染函数:
javascript
// 编译结果相当于:
function render() {
return _c('div',
list.map(item => {
return showItem(item)
? _c('div', { key: item.id }, [_v(item.content)])
: undefined
})
)
}
关键点在于:
- 优先执行v-for:列表遍历永远发生在条件判断之前
- 无法短路优化:即便第一个元素就满足条件,仍会继续遍历剩余1999个
- Key的副作用 :即使节点被
v-if移除,Vue仍会为它们保留内存引用
不只是性能问题:你可能没注意到的坑
坑点1:作用域优先级陷阱
vue
<div v-for="item in list" v-if="item.visible">
<!-- 你以为的item:来自v-for -->
<!-- 实际查找顺序:当前组件实例 -> v-for的item -->
</div>
当组件也有item属性时,这里会优先读取组件属性!Vue官方文档明确警告过这一点,但有多少人仔细读过?
坑点2:过渡动画失效
vue
<transition-group>
<div v-for="item in list" v-if="item.show" :key="item.id">
<!-- 你的动画可能突然失效 -->
</div>
</transition-group>
被v-if移除的节点会直接销毁,无法触发离开动画。这个问题在动态过滤列表时尤其明显。
坑点3:内存泄漏
javascript
const list = ref([
{ id: 1, content: '...', el: document.createElement('div') }
])
当v-if为false时,虽然DOM节点被移除,但item.el这样的原生DOM引用仍然驻留在内存中。
正确姿势:不只是调换顺序那么简单
方案1:外层用计算属性过滤(推荐)
vue
<template>
<div>
<!-- 正确写法1:预过滤 -->
<div v-for="item in filteredList" :key="item.id">
{{ item.content }}
</div>
</div>
</template>
<script>
const filteredList = computed(() => list.value.filter(showItem))
</script>
- 优势*:
- 计算属性有缓存,避免重复计算
- 列表变更时自动触发更新
- 代码可读性更高
方案2:用template包裹(特殊场景)
vue
<template v-for="item in list">
<div v-if="showItem(item)" :key="item.id">
{{ item.content }}
</div>
</template>
注意点:
- 必须写
key在真实元素上 template本身不会渲染DOM节点- 适合需要保留原始数组顺序的场景
避坑指南:资深玩家才知道的细节
- 永远不要把它们写在同一个元素上:这是Vue风格指南明确禁止的
- 大列表必须预过滤:超过500条数据时,性能差异会指数级放大
- 警惕内存引用:过滤时注意深拷贝 vs 浅拷贝的问题
- 组合式API的坑 :在setup中使用
v-for+v-if时,作用域问题会更隐蔽 - 测试时造大数据:开发环境可能只有10条测试数据,上线后才发现性能问题
写在最后
v-if和v-for的组合就像咖啡因和酒精------单独用都没问题,混在一起就可能出事。下次看到这种写法时,不妨问问自己:这里真的需要实时计算吗?能不能提前过滤?数据量会不会增长?
你在项目中还遇到过哪些Vue的"简单用法"引发的血案?评论区聊聊你的踩坑经历。