首頁 >web前端 >Vue.js >vue優先比較:為什麼v-for比v-if高

vue優先比較:為什麼v-for比v-if高

青灯夜游
青灯夜游轉載
2023-03-14 19:44:191672瀏覽

vue為什麼v-for的優先權比v-if的高?以下這篇文章就透過分析原始碼去解答一下這個問題,希望對大家有幫助!

vue優先比較:為什麼v-for比v-if高

有時候有些面試中常會問到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指令,除了記錄在attrsMap attrsList,也會新增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###相關的屬性。除此之外,也可以知道###Vue###對###v-for###和###v-if###是怎麼處理的。 ######(學習影片分享:###vuejs入門教學###、###程式設計基礎影片###)###

以上是vue優先比較:為什麼v-for比v-if高的詳細內容。更多資訊請關注PHP中文網其他相關文章!

陳述:
本文轉載於:juejin.cn。如有侵權,請聯絡admin@php.cn刪除