vue中v-if与v-for的优先级问题

308 阅读3分钟

v-if和v-for哪个优先级更高呢?这是面试官常常问到的一个问题,接下来我将针对此类问题进行详细讲解,如有问题欢迎指出~~

vue2

当v-if与v-for一起使用时,vue2中v-for优先级高于v-if

但也面临着一些问题:

当v-if的值不依赖于v-for时:

<template>
  <ul>
      <li v-for="item in arr" :key="item.id" v-if="flag">{{ arrs.name }}</li>
  </ul>
</template>

  <script>
  export default {
    data() {
      return {
        flag: false,
        arr: [
          { id: 1, name: "刘德华", flag: true },
          { id: 2, name: "张学友", flag: false },
          { id: 2, name: "郭富城", flag: true },
        ],
      };
    }
  };
</script>

如上述代码,如果flag为false,由于v-for优先级高于v-if,导致输出三个注释节点,显然这是不合理的。

image.png

就是说,flag这个判断其实如果是false,循环并不需要执行,但是现在跟v-if一起用,不管flag是否是true,一上来都得等循环完了再if判断,完全是浪费。

解决办法是:

<template>
  <ul>
    <template v-if="flag">
      <li v-for="item in arr" :key="item.id">{{ item.name }}</li>
    </template>
  </ul>
</template>

当flag为false, 最终效果可以看出只循环了一次,避免了性能浪费。

image.png

所以将v-if提前,即便v-for优先级高于v-if,也得根据v-if来判断是否进行循环。

当v-if的值依赖于v-for时:

<template>
  <ul>
    <li v-for="item in arr" :key="item.id" v-if="item.flag">{{ item.name }}</li>
  </ul>
</template>

<script>
export default {
  data() {
    return {
      arr: [
        { id: 1, name: "刘德华", flag: true },
        { id: 2, name: "张学友", flag: false },
        { id: 2, name: "郭富城", flag: true },
      ],
    };
  }
};
</script>

代码并没有逻辑上的错误,同样呈现正常效果,没报错

每次重新渲染的时候,其实我们只需要item.flag为true的列表的一部分,但渲染时都重新遍历了整个列表,这样做会造成性能浪费。

因为<template>上不能使用key,这种方式就不阐述了,按照上述同样方式进行解决:

<template>
  <ul>
    <template v-if="item.flag">
      <li v-for="item in arr" :key="item.id">{{ item.name }}</li>
    </template>
  </ul>
</template>

这样报错就在正常不过了,因为item未曾定义,所以item.flag报错

不卖关子了,正确且不造成性能浪费的方式是计算属性:

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

<script>
export default {
  data() {
    return {
      flag: false,
      arr: [
        { id: 1, name: "刘德华", flag: true },
        { id: 2, name: "张学友", flag: false },
        { id: 2, name: "郭富城", flag: true },
      ],
    };
  },
  computed: {
    newArr() {
      return this.arr.filter((item) => item.flag);
    },
  },
};
</script>

效果一样

vue3

当v-if与v-for一起使用时,vue3中v-if优先级高于v-for

都知道vue3在性能优化方面要强于vue2,那么关于v-if与v-for的优先级问题,vue3是怎么做的呢?

看两张图

图中是vue3代码编译成render函数的结果 有图可以看出当v-if与v-for同级存在时,先执行v-if操作,这也验证了vue3v-if优先级高于v-for。

如图二,我把v-if提前,但编译成的render函数,简直一模一样,未曾变化。这又可以看出vue3底层就是把v-if提前的这个操作,解决了性能浪费问题。 所以这就是vue3比vue2性能方面要好的原因。

测试:

.v2.template-explorer

.vue3-template-explorer

当v-if的值不依赖于v-for时:

上述可以看出vue3v-if优先级高于v-for, 所以不存在性能问题。正常写<li v-for="item in arr" :key="item.id" v-if="flag">即可。

当v-if的值依赖于v-for时:

因为v-if优先级高于v-for的缘故,又会导致被依赖的属性未被定义。 这里有两种方式:

第一种方式:

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

<script setup lang="ts">
import { computed, ref } from "vue";
const arr = ref([
  { id: 1, name: "刘德华", flag: true },
  { id: 2, name: "张学友", flag: false },
  { id: 2, name: "郭富城", flag: true },
]);
const newArr = computed(() => {
  return arr.value.filter((v) => v.flag);
});
</script>

计算属性可以解决问题。正常输出,不报错。

第二种方式:(不推荐)

<template>
  <ul>
    <template v-for="item in newArr" :key="item.id">
      <li v-if="item.flag">{{ item.name }}</li>
    </template>
  </ul>
</template>

vue3官网明确指出,<template>中可以使用key

由此得出上述代码,可能对性能浪费没有帮助, 但在不考虑性能的情况下,此方法可行。

总结

  • vue2中,v-for的优先级高于v-if

  • vue3中,v-if的优先级高于v-for

  • vue2中,解决v-for优先级高于v-if产生的性能浪费的方式有两种: 当v-if不依赖于v-for时,将v-if提前,当v-if依赖于v-for时,使用计算属性

  • vue3中,当v-if不依赖于v-for时,没有性能浪费问题,当v-if依赖于v-for时,使用计算属性解决性能浪费问题。

  • 我的个人博客