Maison > Article > interface Web > Comparaison de priorité : lequel est le plus élevé, v-for ou v-if ?
Lequel a la priorité la plus élevée, v-for ou v-if ? L'article suivant expliquera les priorités de v-for
et v-if
à partir d'exemples réels et du code source. Je pense que vous serez éclairé après l'avoir lu. v-for
和v-if
的优先级,相信你看完后会茅塞顿开的。
1、本身并不建议将v-for和v-if同时使用的。(学习视频分享:vue视频教程)
2、vue2里面v-for比v-if的优先级更高。因为vue2在模板编译的时候会先处理v-for再处理v-if,所以生成的渲染函数会先执行循环,然后在循环里面再执行条件判断。
3、这样做带来的问题就是
对于场景1:<li v-for="user in users" v-if="user.show">
每次重新渲染的时候,都要重新遍历整个列表,其实我们只需要列表的一部分,这样做浪费性能。推荐的做法是,通过计算属性先过滤出我们需要的部分,再去渲染,更高效。
对于场景2: <li 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('ul', _l((users), function (user) { return (user.isActive) ? _c('li', 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 = { 'type': 1, 'tag': 'ul', 'attrsList': [], 'attrsMap': {}, 'children': [{ 'type': 1, 'tag': 'li', 'attrsList': [], 'attrsMap': { 'v-for': 'user in users', 'v-if': 'user.show' }, // v-if解析出来的属性 'if': 'user.show', 'ifConditions': [{ 'exp': 'user.show', 'block': // 指向el自身 }], // v-for解析出来的属性 'for': 'users', 'alias': 'user', 'iterator1': 'index', 'parent': // 指向其父节点 'children': [ 'type': 2, 'expression': '_s(user)' 'text': '{{user}}', 'tokens': [ {'@binding':'user'}, ] ] }] }
compiler/codegen/index.js
// generate 调用 genElement const code = ast ? genElement(ast, state) : '_c("div")' // 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 === 'template' && !el.slotTarget && !state.pre) { return genChildren(el, state) || 'void 0' } else if (el.tag === 'slot') { return genSlot(el, state) } else { // 最后这里处理标签等 const children = el.inlineTemplate ? null : genChildren(el, state, true) code = `_c('${el.tag}'${ data ? `,${data}` : '' // data }${ children ? `,${children}` : '' // 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}` : '' const iterator2 = el.iterator2 ? `,${el.iterator2}` : '' el.forProcessed = true // 下次递归调用genElement的时候就不会重复处理v-for了 return `${altHelper || '_l'}((${exp}),` + `function(${alias}${iterator1}${iterator2}){` // 这里处理完了v-for,递归调用genElement继续处理v-if `return ${(altGen || genElement)(el, state)}` + '})'
最终会生成类似如下的代码返回出去
<li v-for="user in users" v-if="user.show">
🎜🎜Every Chaque fois que nous effectuons un nouveau rendu, nous devons parcourir à nouveau la liste entière. En fait, nous n'avons besoin que d'une partie de la liste, ce qui gaspille les performances. L'approche recommandée consiste à filtrer les pièces dont nous avons besoin via des attributs calculés, puis à les restituer, ce qui est plus efficace. 🎜🎜Pour le scénario 2 : <li v-for="user in users" v-if="globalShow">
🎜🎜En fait, si le jugement de globalShow est faux, la boucle le fait pas besoin d'être exécuté. Mais maintenant, lorsqu'elle est utilisée avec v-if, la boucle doit être exécutée, que globalShow soit vrai ou non, ce qui est un gaspillage complet. L'approche recommandée consiste à déplacer v-if vers le conteneur ul. 🎜🎜4. Il est à noter que pour le changement avec rupture de vue3, v-if a une priorité plus élevée que v-for dans vue3, donc s'il est utilisé en même temps, pour le scénario 1, l'utilisateur n'a pas encore été utilisé à cette fois, v-if="user.show" signalera une erreur🎜🎜5. Généralement, si nous utilisons eslint, il nous signalera également une erreur. La règle correspondante est : vue/no-use-v-if. -with-v-for🎜_l((users), function(user, index) { // 如果有v-if 前面就会有个条件判断,如user.isActive return (user.isActive) ? _c('li', user.name) : _e() });🎜La fonction de rendu générée est la suivante🎜rrreee 🎜Comme le montre la fonction de rendu générée ci-dessus, elle sera exécutée en premier
_l
traverse user
et y effectue un jugement conditionnel🎜src/compiler/index.js
🎜rrreee🎜génère du code basé sur ast. Si c'est le modèle ci-dessus, l'ast généré est simplifié comme suit🎜rrreee🎜compiler/codegen/index js
🎜rrreee🎜généra éventuellement un code similaire à celui-ci et le renverra
🎜rrreee. 🎜 (Partage de vidéos d'apprentissage : 🎜développement web front-end🎜, 🎜Vidéo de programmation de base🎜)🎜Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!