v-if 和 v-for 哪个优先级更高?如果两个同时出现,应该怎么优化得 到更好的性能?

573 阅读4分钟

看到这道面试题,不知道大有没有给出正确的答案,下面我们来分析一下。

那么我依然建议大家不要仅仅的只是去网上找答案。我们最终目标是自己要从源码中去找到答案,我们要去哪儿找了?

大家就去这个代码生成的地方,相信大家在学编译器的时候应该涉及到。比如我们写一个例子,最终写的这个 template,它一定会生成一个代码,一个渲染函数,比如我们写一个

,如下代码:

<p v-for="item in items" v-if="condition"></p>

上面的 p 标签有循环和判断,判断一下某个内容要不要显示。那么这就有问题了,如果这两个指令放到一起,会不会有问题?

我们先去看一下这个代码生成这里面,就是最后生成的这个代码到底是怎么样子,我们做了一个简单的测试。

<!DOCTYPE html>
<html>
<head>
  <meta charset="utf-8" />
  <title></title>
  <script src="vue/vue.js"></script>
</head>
<body>
<div id="app">
  <div v-for="item in listdata" v-if="isShow(item)">姓名:{{item.name}},年龄:{{item.age}}</div>
</div>
<script type="text/javascript">
  // 创建实例
  var app = new Vue({
    el: '#app',
    data() {
      return {
        listdata: [{
          name: '张三',
          age: 17
        },
          {
            name: '李四',
            age: 18
          }, {
            name: '王五',
            age: 19
          }
        ],
        visible: true
      }
    },
    computed: {


    },
    methods: {
      isShow(item) {
        try {
          console.log("判断一次=", JSON.stringify(item));
        } catch (e) {
          console.log("出错", e);
        }
        return this.visible;
      }
    }
  });
  console.log("渲染函数", app.$options.render);
</script>
</body>
</html>

运行结果如下:

image.png

生成的渲染函数:

 

渲染函数 ƒ anonymous(
) {
with(this){return _c('div',{attrs:{"id":"app"}},_l((listdata),function(item){return (isShow)?_c('div',[_v("姓名:"+_s(item.name)+",年龄:"+_s(item.age))]):_e()}),0)}
}

注意:_l 方法,它是列表渲染的函数,就是将来会循环输出所有 listdata,然后每循环一次执行的具体的函数会是后面的这个工厂函数。在这个工厂函数里面,return 才是最终的结果,这里头会判断条件。这个东西告诉我们什么呢?也就是说当这两者同时放在一起的时候。这个 isShow 一定是在内部的那个,然后 for 循环一定是在外面的。从这个表面上看出来,这个循环会在外面先执行了。

第二个实验就是当它们不同级的时候,因为这也是通常大家在处理这种问题的时候的一个正确解决方案。

<template v-if="isShow">
    <div v-for="item in listdata">姓名:{{item.name}},年龄:</div>
</template>

生成的渲染函数:

渲染函数 ƒ anonymous(
) {
with(this){return _c('div',{attrs:{"id":"app"}},[(isShow)?_l((listdata),function(item){return _c('div',[_v("姓名:"+_s(item.name)+",年龄:")])}):_e()],2)}
}

在这种情况下先判断条件,然后才会循环。

大家看这边再生成的这个渲染函数是什么?它会先判断这个 isShow 条件,如果当前这个条件成立了,我们才会执行这个列表渲染循环的这个语句,否则将是一个空函,_e 是一个空函数。

通过对比,显然这种方式会更好一些。

因为如果这个 false 的话,我们可能压根就不用做任何循环,就给我们一个机会直接跳过所有循环了。但是如果像上面这种写法的话,大家显然会发现不好。因为它不管怎么样都会做循环,然后它在循环里头对每一项去做这个 isShow 的判断,显然是不合理的。

所以就给大家一个建议,如果你在写程序写代码的时候,这两者同时出现,你一定要考虑把它提取出来了。

最后我要给出结论了,如果在面试中,遇到这题,我们应该怎回答呢?

我们首先结论是 v-for 的优先级是高于 v-if 的,当然这也不是乱说的,通过前面的分析。同时以可以通过源码我们也能找到答案。

源码中找答案 compiler/codegen/index.js。

image.png

注意看,在做判断的时候,我们实际上是有先后顺序的,会先看 staticRoot  静态节点,再看once ,最后才会处理这个 for 循环,也就是在处理 for 的时候,其实是优先于这个 if 的。所以最后那个代码生成的时候,这个 for 是不是在 if 的前面呀?这就是它的优先级。

大家在答这道题的时候,你要告诉这个面试官,我是怎么知道的,你一定不能说我是从网上找到答案,这是最垃圾的答案。

最好的答案应该是你亲自经过了实验,你写了一个测试的代码,去源码中找到了答案,所以你知道 v-for 的优先级是高 v-if。它们会影响这个渲染函数的生成结果是吧

v-for 和 v-if 两个同时出现,则将来生成的这个 _l 的这个循环,一定会包含着所有的条件判断,也就是说我们将来无论如何都会先执行循环再判断,这样的话,我们无可避免的去执行循环就浪费了性能了。

但是如果我们把它分层写开,比如我把这个条件写到外围,那这样的话我们会先判断条件,再尝试着看看是不是需要生成循环,这样的话我有可能直接避免掉循环。

如果条件出现在循环内部,可通过计算属性提前过滤掉那些不需要显示的项。