关键点
在 Vue 3 中,v-if 的优先级 实际上要高于 v-for,所以 v-if 和 v-for 一起使用是没问题的。但是官方还是不推荐把 v-if 和 v-for 一起使用
在 Vue 2 中,v-for 比 v-if 具有更高的优先级,即使要渲染的列表中,只有一小部分元素是需要被渲染出来的,Vue 也得在每次重渲染的时候遍历整个列表,然后才能判断哪些元素是需要被渲染的,数据量比较大的情况下,开销会较大。
Vue 2 中的 v-if 和 v-for 一起使用的具体表现
我们使用 vue-template-compiler 这个包来看解析一下模板,以下面代码为例:
const compiler = require('vue-template-compiler')
const res = compiler.compile(`<div>
<div v-for="(item, index) in renderedList" v-if="item.show">被渲染的元素</div>
</div>`)
我们只需要看 render 这个属性的值就好了,因为它决定了如何创建 VNodes。
with(this) {
return _c('div',
_l(renderedList, (item, index) => {
return item.show ? _c('div', [_v("被渲染的元素")]) : _e();
})
);
}
相关方法:
_c: 创建 VNode 节点
_l: 处理 v-for 指令,第一个接收的参数为渲染数据,第二接收的参数是数据处理回调
_v: 创建文本节点
_e: 创建一个空的 VNode 节点
我们可以看到,当 v-for 与 v-if 一起使用的时候,_l 是先执行的,而 v-if 的处理条件是在 _l 的数据处理回调里面的。
所以我们可以得出,在 Vue 2 中,v-for 的优先级是要比 v-if 优先级要高的
Vue 3 中的 v-if 和 v-for 一起使用的具体表现
我们使用 @vue/compiler-sfc 这个包来看解析一下模板,以下面代码为例:
const {
compileTemplate,
} = require('@vue/compiler-sfc')
function compile(opts) {
return compileTemplate({
...opts,
id: '',
})
}
const source = `<div>
<div v-for="(item, index) in renderedList" v-if="item.show">需要被渲染的元素</div>
</div>`
const result = compile({ filename: 'example.vue', source })
我们只需要看 code 这个属性的值就好了,因为它决定了如何创建 VNodes。
import { renderList as _renderList, Fragment as _Fragment, openBlock as _openBlock, createElementBlock as _createElementBlock, createCommentVNode as _createCommentVNode } from "vue"
export function render(_ctx, _cache) {
return (_openBlock(), _createElementBlock("div", null, [
// 关键点,v-if 处理在前,v-for 处理在后
(_ctx.item.show)
? (_openBlock(true), _createElementBlock(_Fragment, { key: 0 }, _renderList(_ctx.renderedList, (item, index) => {
return (_openBlock(), _createElementBlock("div", null, "需要被渲染的元素"))
}), 256 /* UNKEYED_FRAGMENT */))
: _createCommentVNode("v-if", true)
]))
}
关键方法:
_createElementBlock: 创建 VNode 节点
_renderList: 处理 v-for 指令,第一个接收的参数为渲染数据,第二接收的参数是数据处理回调
_createCommentVNode: 创建注释节点
我们可以看到,当 v-for 与 v-if 一起使用的时候,v-if 的判断是先执行的,而 v-for 的处理是在 v-if 条件满足后再进行处理的。
为什么 Vue 3 还是不推荐把 v-for 和 v-if 一起使用?
来看一个案例:
<ul>
<li
v-for="user in users"
v-if="user.isActive"
:key="user.id"
>
{{ user.name }}
</li>
</ul>
放到浏览器上去执行的时候,就会发现报错了:
原因:v-if 的优先级要比 v-for 要高,所以,这里 v-if 条件实际上是拿不到 v-for 中遍历出来的变量 user 的
一般处理方法
对于这个案例:
<ul>
<li
v-for="user in users"
v-if="user.isActive"
:key="user.id"
>
{{ user.name }}
</li>
</ul>
有 2 个解决方法:
1、将遍历数据用 computed 先过滤一遍:
<template>
<ul>
<li
v-for="user in activedUser"
:key="user.id"
>
{{ user.name }}
</li>
</ul>
</template>
<script lang="ts" setup>
import { ref, computed } from 'vue'
const users = ref([
{
isActive: true,
id: 1,
name: 'zs'
}
])
const activedUser = computed(() => {
return users.value.filter(user => user.isActive)
})
</script>
2、将 v-if 放到 v-for 的元素下,并且防止有多余的元素,使用 template 进行包裹
<ul>
<template v-for="user in users" :key="user.id">
<li v-if="user.isActive">
{{ user.name }}
</li>
</template>
</ul>