Warum hat v-for eine höhere Priorität als v-if in vue? Der folgende Artikel wird diese Frage durch die Analyse des Quellcodes beantworten. Ich hoffe, dass er für alle hilfreich ist!
Manchmal fragen wir in manchen Interviews, wer die höhere Priorität hat: v-for
oder v-if
Hier beantworten wir diese Frage durch Analyse der Quelle Code. Frage. v-for
与v-if
谁的优先级高,这里就通过分析源码去解答一下这个问题。
下面的内容是在 当我们谈及v-model,我们在讨论什么?的基础上分析的,所以阅读下面内容之前可先看这篇文章。
以下面的例子出发分析:
new Vue({ el:'#app', template:` <ul> <li v-for="(item,index) in items" v-if="index!==0"> {{item}} </li> </ul> ` })
从上篇文章可以知道,编译有三个步骤
我们再次顺着这三个步骤对上述例子进行分析。
parse过程中,会对模板使用大量的正则表达式去进行解析。开头的例子会被解析成以下AST
节点:
// 其实ast有很多属性,我这里只展示涉及到分析的属性 ast = { 'type': 1, 'tag': 'ul', 'attrsList': [], attrsMap: {}, 'children': [{ 'type': 1, 'tag': 'li', 'attrsList': [], 'attrsMap': { 'v-for': '(item,index) in data', 'v-if': 'index!==0' }, // v-if解析出来的属性 'if': 'index!==0', 'ifConditions': [{ 'exp': 'index!==0', 'block': // 指向el自身 }], // v-for解析出来的属性 'for': 'items', 'alias': 'item', 'iterator1': 'index', 'parent': // 指向其父节点 'children': [ 'type': 2, 'expression': '_s(item)' 'text': '{{item}}', 'tokens': [ {'@binding':'item'}, ] ] }] }
对于v-for
指令,除了记录在attrsMap
和attrsList
,还会新增for
(对应要遍历的对象或数组),alias
,iterator1
,iterator2
对应v-for
指令绑定内容中的第一,第二,第三个参数,开头的例子没有第三个参数,因此没有iterator2
属性。
对于v-if
指令,把v-if
指令中绑定的内容取出放在if
中,与此同时初始化ifConditions
属性为数组,然后往里面存放对象:{exp,block}
,其中exp
存放v-if
指令中绑定的内容,block
指向el
。
optimize 过程在此不做分析,因为本例子没有静态节点。
上一篇文章从const code = generate(ast, options)
开始分析过其生成代码的过程,generate
内部会调用genElement
用来解析el
,也就是AST
语法树。我们来看一下genElement
的源码:
export function genElement (el: ASTElement, state: CodegenState): string { if (el.parent) { el.pre = el.pre || el.parent.pre } if (el.staticRoot && !el.staticProcessed) { return genStatic(el, state) } else if (el.once && !el.onceProcessed) { return genOnce(el, state) // 其实从此处可以初步知道为什么v-for优先级比v-if高, // 因为解析ast树生成渲染函数代码时,会先解析ast树中涉及到v-for的属性 // 然后再解析ast树中涉及到v-if的属性 // 而且genFor在会把el.forProcessed置为true,防止重复解析v-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 { // component or element let code if (el.component) { code = genComponent(el.component, el, state) } else { let data if (!el.plain || (el.pre && state.maybeComponent(el))) { data = genData(el, state) } const children = el.inlineTemplate ? null : genChildren(el, state, true) code = `_c('${el.tag}'${ data ? `,${data}` : '' // data }${ children ? `,${children}` : '' // children })` } // module transforms for (let i = 0; i < state.transforms.length; i++) { code = state.transforms[i](el, code) } return code } }
接下来依次看看genFor
和genIf
的函数源码:
export function genFor (el, state , altGen, altHelper) { const exp = el.for const alias = el.alias const iterator1 = el.iterator1 ? `,${el.iterator1}` : '' const iterator2 = el.iterator2 ? `,${el.iterator2}` : '' el.forProcessed = true // avoid recursion return `${altHelper || '_l'}((${exp}),` + `function(${alias}${iterator1}${iterator2}){` + `return ${(altGen || genElement)(el, state)}` + //递归调用genElement '})' }
在我们的例子里,当他处理li
的ast
树时,会先调用genElement
,处理到for
属性时,此时forProcessed
为虚值,此时调用genFor
处理li
树中的v-for
相关的属性。然后再调用genElement
处理li
树,此时因为forProcessed
在genFor
中已被标记为true
。因此genFor
不会被执行,继而执行genIf
处理与v-if
相关的属性。
export function genIf (el,state,altGen,altEmpty) { el.ifProcessed = true // avoid recursion // 调用genIfConditions主要处理el.ifConditions属性 return genIfConditions(el.ifConditions.slice(), state, altGen, altEmpty) } function genIfConditions (conditions, state, altGen, altEmpty) { if (!conditions.length) { return altEmpty || '_e()' // _e用于生成空VNode } const condition = conditions.shift() if (condition.exp) { //condition.exp即v-if绑定值,例子中则为'index!==0' // 生成一段带三目运算符的js代码字符串 return `(${condition.exp})?${ genTernaryExp(condition.block) }:${ genIfConditions(conditions, state, altGen, altEmpty) }` } else { return `${genTernaryExp(condition.block)}` } // v-if with v-once should generate code like (a)?_m(0):_m(1) function genTernaryExp (el) { return altGen ? altGen(el, state) : el.once ? genOnce(el, state) : genElement(el, state) } }
参考 前端进阶面试题详细解答
最后,经过codegen生成的js代码如下:
function render() { with(this) { return _c('ul', _l((items), function (item, index) { return (index !== 0) ? _c('li') : _e() }), 0) } }
其中:
_c
: 调用 createElement
去创建 VNode
_l
: renderList
函数,主要用来渲染列表
_e
: createEmptyVNode
函数,主要用来创建空VNode
为什么v-for的优先级比v-if的高?总结来说是编译有三个过程,parse
->optimize
->codegen
。在codegen
过程中,会先解析AST
树中的与v-for
相关的属性,再解析与v-if
相关的属性。除此之外,也可以知道Vue
对v-for
和v-if
AST
-Knoten geparst: 🎜rrreee🎜Für die v-for
-Direktive, zusätzlich zu Datensätzen in attrsMap
und attrsList
und fügt außerdem for
(entsprechend dem zu durchlaufenden Objekt oder Array), alias
, iterator1 hinzu. code>, <code>iterator2 entspricht dem ersten, zweiten und dritten Parameter im Inhalt der <code>v-for
-Anweisung. Das Beispiel am Anfang verfügt nicht über den dritten Parameter Es gibt keine iterator2
-Eigenschaft. 🎜🎜Für die v-if
-Anweisung nehmen Sie den in der v-if
-Anweisung gebundenen Inhalt heraus und fügen ihn in if
ein Gleichzeitig initialisierenifConditions ist ein Array, und dann wird das Objekt darin gespeichert: {exp,block
, wobei exp
v-if Der in der Anweisung gebundene Inhalt block
zeigt auf el
. 🎜🎜optimize Der Prozess wird hier nicht analysiert, da es in diesem Beispiel keine statischen Knoten gibt. 🎜const code = generic(ast, options)
analysiert Beim Generieren von Code ruft generate
intern genElement
auf, um el
zu analysieren, bei dem es sich um den AST
-Syntaxbaum handelt. Werfen wir einen Blick auf den Quellcode von genElement
: 🎜rrreee🎜 Als nächstes werfen wir einen Blick auf den Funktionsquellcode von genFor
und genIf
: 🎜rrreee🎜Wenn er in unserem Beispiel den ast
-Baum von li
verarbeitet, ruft er zuerst genElement
auf und verarbeitet dann den for
-Attribut, zu diesem Zeitpunkt ist forProcessed
ein virtueller Wert. Rufen Sie zu diesem Zeitpunkt genFor
auf, um den v-for
zu verarbeiten > im Zusammenhang mit der li
-Eigenschaft. Rufen Sie dann genElement
auf, um den li
-Baum zu verarbeiten, da forProcessed
in genFor
true markiert wurde /code>. Daher wird genFor
nicht ausgeführt und dann wird genIf
ausgeführt, um die mit v-if
verbundenen Attribute zu verarbeiten. 🎜rrreee🎜ReferenzDetaillierte Antworten auf fortgeschrittene Front-End-Interviewfragen🎜🎜🎜Schließlich lautet der von codegen generierte js-Code wie folgt: 🎜rrreee🎜Unter ihnen: 🎜_c
: Rufen Sie createElement
auf, um VNode
🎜 zu erstellen _l
: renderList
-Funktion, wird hauptsächlich zum Rendern von Listen verwendet🎜_e
: createEmptyVNode
-Funktion, hauptsächlich Wird zum Erstellen eines leerenVNode🎜parse
->optimize
->codegen
. Im codegen
-Prozess werden zuerst die Attribute analysiert, die sich auf v-for
im AST
-Baum beziehen, und dann die Attribute, die sich auf v-if
Verwandte Attribute. Darüber hinaus erfahren Sie auch, wie Vue
mit v-for
und v-if
umgeht. 🎜🎜 (Teilen von Lernvideos: 🎜Vuejs-Einführungs-Tutorial🎜, 🎜Grundlegendes Programmiervideo🎜)🎜Das obige ist der detaillierte Inhalt vonVue-Prioritätsvergleich: Warum v-for höher ist als v-if. Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!