Heim >Web-Frontend >View.js >Vue-Prioritätsvergleich: Warum v-for höher ist als v-if

Vue-Prioritätsvergleich: Warum v-for höher ist als v-if

青灯夜游
青灯夜游nach vorne
2023-03-14 19:44:191675Durchsuche

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!

Vue-Prioritätsvergleich: Warum v-for höher ist als v-if

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-forv-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语法树
  • optimize : 优化语法树,主要时标记静态节点,提高更新页面的性能 【相关推荐:vuejs视频教程web前端开发
  • codegen : 生成js代码,主要是render函数和staticRenderFns函数

我们再次顺着这三个步骤对上述例子进行分析。

parse

parse过程中,会对模板使用大量的正则表达式去进行解析。开头的例子会被解析成以下AST节点:

// 其实ast有很多属性,我这里只展示涉及到分析的属性
ast = {
  &#39;type&#39;: 1,
  &#39;tag&#39;: &#39;ul&#39;,
  &#39;attrsList&#39;: [],
  attrsMap: {},
  &#39;children&#39;: [{
    &#39;type&#39;: 1,
    &#39;tag&#39;: &#39;li&#39;,
    &#39;attrsList&#39;: [],
    &#39;attrsMap&#39;: {
      &#39;v-for&#39;: &#39;(item,index) in data&#39;,
      &#39;v-if&#39;: &#39;index!==0&#39;
     },
     // v-if解析出来的属性
    &#39;if&#39;: &#39;index!==0&#39;,
    &#39;ifConditions&#39;: [{
      &#39;exp&#39;: &#39;index!==0&#39;,
      &#39;block&#39;: // 指向el自身
    }],
    // v-for解析出来的属性
    &#39;for&#39;: &#39;items&#39;,
    &#39;alias&#39;: &#39;item&#39;,
    &#39;iterator1&#39;: &#39;index&#39;,

    &#39;parent&#39;: // 指向其父节点
    &#39;children&#39;: [
      &#39;type&#39;: 2,
      &#39;expression&#39;: &#39;_s(item)&#39;
      &#39;text&#39;: &#39;{{item}}&#39;,
      &#39;tokens&#39;: [
        {&#39;@binding&#39;:&#39;item&#39;},
      ]
    ]
  }]
}

对于v-for指令,除了记录在attrsMapattrsList,还会新增for(对应要遍历的对象或数组),aliasiterator1,iterator2对应v-for指令绑定内容中的第一,第二,第三个参数,开头的例子没有第三个参数,因此没有iterator2属性。

对于v-if指令,把v-if指令中绑定的内容取出放在if中,与此同时初始化ifConditions属性为数组,然后往里面存放对象:{exp,block},其中exp存放v-if指令中绑定的内容,block指向el

optimize 过程在此不做分析,因为本例子没有静态节点。

codegen

上一篇文章从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 === &#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 {
    // 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(&#39;${el.tag}&#39;${        data ? `,${data}` : &#39;&#39; // data      }${        children ? `,${children}` : &#39;&#39; // children      })`
    }
    // module transforms
    for (let i = 0; i < state.transforms.length; i++) {
      code = state.transforms[i](el, code)
    }
    return code
  }
}

接下来依次看看genForgenIf的函数源码:

export function genFor (el, state , altGen, altHelper) {
  const exp = el.for
  const alias = el.alias
  const iterator1 = el.iterator1 ? `,${el.iterator1}` : &#39;&#39;
  const iterator2 = el.iterator2 ? `,${el.iterator2}` : &#39;&#39;

  el.forProcessed = true // avoid recursion
  return `${altHelper || &#39;_l&#39;}((${exp}),` + 
    `function(${alias}${iterator1}${iterator2}){` +
      `return ${(altGen || genElement)(el, state)}` + //递归调用genElement
    &#39;})&#39;
}

在我们的例子里,当他处理liast树时,会先调用genElement,处理到for属性时,此时forProcessed为虚值,此时调用genFor处理li树中的v-for相关的属性。然后再调用genElement处理li树,此时因为forProcessedgenFor中已被标记为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 || &#39;_e()&#39; // _e用于生成空VNode
  }

  const condition = conditions.shift()
  if (condition.exp) { //condition.exp即v-if绑定值,例子中则为&#39;index!==0&#39;
    // 生成一段带三目运算符的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(&#39;ul&#39;, _l((items), function (item, index) {
      return (index !== 0) ? _c(&#39;li&#39;) : _e()
    }), 0)
  }
}

其中:

  • _c: 调用 createElement 去创建 VNode

  • _l: renderList函数,主要用来渲染列表

  • _e: createEmptyVNode函数,主要用来创建空VNode

总结

为什么v-for的优先级比v-if的高?总结来说是编译有三个过程,parse->optimize->codegen。在codegen过程中,会先解析AST树中的与v-for相关的属性,再解析与v-if相关的属性。除此之外,也可以知道Vuev-forv-if

Der folgende Inhalt wird auf der Grundlage von „Wenn wir über V-Modell sprechen, wovon reden wir?“ analysiert, sodass Sie diesen Artikel lesen können, bevor Sie den folgenden Inhalt lesen.

Weiter mit der Kompilierung beginnen

Beginnen Sie die Analyse mit dem folgenden Beispiel: rrreeeWie Sie aus dem vorherigen Artikel wissen können, Die Kompilierung besteht aus drei Schritten
  • parse: Analysiert die Vorlagenzeichenfolge, um einen AST-Syntaxbaum zu generieren
  • optimize: Optimiert die Syntaxbaum, der hauptsächlich statische Knoten markiert. Verbessert die Leistung aktualisierter Seiten. [Verwandte Empfehlungen: vuejs-Video-Tutorial, Web-Front-End-Entwicklung🎜]
  • codegen: Generieren Sie JS-Code, hauptsächlich die Render-Funktion und die staticRenderFns-Funktion
🎜Wir werden das obige Beispiel analysieren, indem wir diese drei Schritte noch einmal ausführen. 🎜

parse

🎜parseDabei wird eine große Anzahl regulärer Ausdrücke zum Parsen verwendet Vorlage. Das Beispiel am Anfang wird in die folgenden 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. 🎜

codegen

🎜Der vorherige Artikel wurde ab 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🎜

Zusammenfassung

🎜Warum hat v-for eine höhere Priorität als v -Wenn? Zusammenfassend gibt es beim Kompilieren drei Prozesse: 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!

Stellungnahme:
Dieser Artikel ist reproduziert unter:juejin.cn. Bei Verstößen wenden Sie sich bitte an admin@php.cn löschen