看到这道面试题,不知道大有没有给出正确的答案,下面我们来分析一下。
那么我依然建议大家不要仅仅的只是去网上找答案。我们最终目标是自己要从源码中去找到答案,我们要去哪儿找了?
大家就去这个代码生成的地方,相信大家在学编译器的时候应该涉及到。比如我们写一个例子,最终写的这个 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>
运行结果如下:
生成的渲染函数:
渲染函数 ƒ 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。
注意看,在做判断的时候,我们实际上是有先后顺序的,会先看 staticRoot 静态节点,再看once ,最后才会处理这个 for 循环,也就是在处理 for 的时候,其实是优先于这个 if 的。所以最后那个代码生成的时候,这个 for 是不是在 if 的前面呀?这就是它的优先级。
大家在答这道题的时候,你要告诉这个面试官,我是怎么知道的,你一定不能说我是从网上找到答案,这是最垃圾的答案。
最好的答案应该是你亲自经过了实验,你写了一个测试的代码,去源码中找到了答案,所以你知道 v-for 的优先级是高 v-if。它们会影响这个渲染函数的生成结果是吧
v-for 和 v-if 两个同时出现,则将来生成的这个 _l 的这个循环,一定会包含着所有的条件判断,也就是说我们将来无论如何都会先执行循环再判断,这样的话,我们无可避免的去执行循环就浪费了性能了。
但是如果我们把它分层写开,比如我把这个条件写到外围,那这样的话我们会先判断条件,再尝试着看看是不是需要生成循环,这样的话我有可能直接避免掉循环。
如果条件出现在循环内部,可通过计算属性提前过滤掉那些不需要显示的项。