优先级比较:v-for 和 v-if哪个更高?

v-for 和 v-if哪个优先级更高?下面本篇文章就从实际例子和源码讲解v-forv-if的优先级,相信你看完后会茅塞顿开的。

优先级比较:v-for 和 v-if哪个更高?

结论

1、本身并不建议将v-for和v-if同时使用的。(学习视频分享:vue视频教程)

2、vue2里面v-for比v-if的优先级更高。因为vue2在模板编译的时候会先处理v-for再处理v-if,所以生成的渲染函数会先执行循环,然后在循环里面再执行条件判断。

3、这样做带来的问题就是

对于场景1: v-for="user in users" v-if="user.show">

每次重新渲染的时候,都要重新遍历整个列表,其实我们只需要列表的一部分,这样做浪费性能。推荐的做法是,通过计算属性先过滤出我们需要的部分,再去渲染,更高效。

对于场景2: v-for="user in users" v-if="globalShow">

globalShow这个判断其实如果是false,循环并不需要执行,但是现在跟v-if一起用,不管globalShow是否是true都要执行循环,完全是浪费。推荐的做法是将v-if上移到ul容器。

4、需要注意的是,vue3的breaking change,在vue3中v-if的优先级比v-for高,所以如果同时使用的话,对于场景1,这个时候user还没有,v-if="user.show"就会报错

5、一般我们如果有用eslint,也会给我们报错,对应的规则是:vue/no-use-v-if-with-v-for

实际例子

例如:以下的模板,将会生成下面的渲染函数

<ul>
    <li v-for="user in users" v-if="user.isActive" :key="user.id">
        {{ user.name }}
    </li>
</ul>

生成的渲染函数如下

with(this) {
    return _c(&#39;ul&#39;, _l((users), function (user) {
        return (user.isActive) ? _c(&#39;li&#39;, user.name) : _e()
    }), 0)
}

从上面生成的渲染函数可以看出,会先执行_l遍历user,在里面进行条件判断

源码

处理v-if和v-for的源码

src/compiler/index.js

// 模板解析,生成ast树
const ast = parse(template.trim(), options)
if (options.optimize !== false) {
    optimize(ast, options)
}
const code = generate(ast, options)

根据ast生成代码,假如是上面的模板,生成的ast简化后如下

// 可以看出v-for和v-if都解析出来了
 ast = {
     &#39;type&#39;: 1,
     &#39;tag&#39;: &#39;ul&#39;,
     &#39;attrsList&#39;: [],
     &#39;attrsMap&#39;: {},
     &#39;children&#39;: [{
     &#39;type&#39;: 1,
     &#39;tag&#39;: &#39;li&#39;,
     &#39;attrsList&#39;: [],
     &#39;attrsMap&#39;: {
         &#39;v-for&#39;: &#39;user in users&#39;,
         &#39;v-if&#39;: &#39;user.show&#39;
     },
     // v-if解析出来的属性
     &#39;if&#39;: &#39;user.show&#39;,
     &#39;ifConditions&#39;: [{
         &#39;exp&#39;: &#39;user.show&#39;,
         &#39;block&#39;: // 指向el自身
     }],
     // v-for解析出来的属性
     &#39;for&#39;: &#39;users&#39;,
     &#39;alias&#39;: &#39;user&#39;,
     &#39;iterator1&#39;: &#39;index&#39;,
     &#39;parent&#39;: // 指向其父节点
     &#39;children&#39;: [
         &#39;type&#39;: 2,
         &#39;expression&#39;: &#39;_s(user)&#39;
         &#39;text&#39;: &#39;{{user}}&#39;,
         &#39;tokens&#39;: [
             {&#39;@binding&#39;:&#39;user&#39;},
         ]
      ]
     }]
 }

compiler/codegen/index.js

// generate 调用 genElement
const code = ast ? genElement(ast, state) : &#39;_c("div")&#39;
// genElement里面的处理
if (el.staticRoot && !el.staticProcessed) {
return genStatic(el, state)
} else if (el.once && !el.onceProcessed) {
return genOnce(el, state)
// 从这可以看出来,先执行genFor,处理v-for指令,在genFor里面会递归调用genElement,继续处理v-if,genFor会将forProcessed设为true,这样下次进来的时候就不会处理for了
} else if (el.for && !el.forProcessed) {
return genFor(el, state)
} else if (el.if && !el.ifProcessed) {
return genIf(el, state)
} else if (el.tag === &#39;template&#39; && !el.slotTarget && !state.pre) {
return genChildren(el, state) || &#39;void 0&#39;
} else if (el.tag === &#39;slot&#39;) {
return genSlot(el, state)
} else {
// 最后这里处理标签等
const children = el.inlineTemplate ? null : genChildren(el, state, true)
code = `_c(&#39;${el.tag}&#39;${
data ? `,${data}` : &#39;&#39; // data
}${
children ? `,${children}` : &#39;&#39; // children
})`
}

// genFor的代码
const exp = el.for // 对应上面ast的 for: users
const alias = el.alias // alias: user
// iterator1 对应v-for的(item,key,index) in items的key
// iterator2 对应的是index
// 通常我们遍历数组 key就是index
// 假如我们遍历的是对象 key就是对象的key,index就是遍历的索引
const iterator1 = el.iterator1 ? `,${el.iterator1}` : &#39;&#39;
const iterator2 = el.iterator2 ? `,${el.iterator2}` : &#39;&#39;
el.forProcessed = true // 下次递归调用genElement的时候就不会重复处理v-for了
return `${altHelper || &#39;_l&#39;}((${exp}),` +
`function(${alias}${iterator1}${iterator2}){`

// 这里处理完了v-for,递归调用genElement继续处理v-if
`return ${(altGen || genElement)(el, state)}` +
&#39;})&#39;

最终会生成类似如下的代码返回出去

_l((users), function(user, index) {
    // 如果有v-if 前面就会有个条件判断,如user.isActive
    return (user.isActive) ? _c(&#39;li&#39;, user.name) : _e()
});

(学习视频分享:web前端开发、编程基础视频)

以上就是优先级比较:v-for 和 v-if哪个更高?的详细内容,更多请关注其它相关文章!