vue為什么v-for的優(yōu)先級比v-if的高?下面本篇文章就通過分析源碼去解答一下這個問題,希望對大家有所幫助!
(資料圖片)
有時候有些面試中經(jīng)常會問到v-for與v-if誰的優(yōu)先級高,這里就通過分析源碼去解答一下這個問題。
下面的內(nèi)容是在 當(dāng)我們談及v-model,我們在討論什么?的基礎(chǔ)上分析的,所以閱讀下面內(nèi)容之前可先看這篇文章。
以下面的例子出發(fā)分析:
new Vue({ el:"#app", template:` <ul> <li v-for="(item,index) in items" v-if="index!==0"> {{item}} </li> </ul> `})從上篇文章可以知道,編譯有三個步驟
parse: 解析模板字符串生成 AST語法樹optimize: 優(yōu)化語法樹,主要時標(biāo)記靜態(tài)節(jié)點,提高更新頁面的性能 【相關(guān)推薦:vuejs視頻教程、web前端開發(fā)】codegen: 生成js代碼,主要是render函數(shù)和staticRenderFns函數(shù)我們再次順著這三個步驟對上述例子進(jìn)行分析。
parse過程中,會對模板使用大量的正則表達(dá)式去進(jìn)行解析。開頭的例子會被解析成以下AST節(jié)點:
// 其實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": // 指向其父節(jié)點 "children": [ "type": 2, "expression": "_s(item)" "text": "{{item}}", "tokens": [ {"@binding":"item"}, ] ] }]}對于v-for指令,除了記錄在attrsMap和attrsList,還會新增for(對應(yīng)要遍歷的對象或數(shù)組),alias,iterator1,iterator2對應(yīng)v-for指令綁定內(nèi)容中的第一,第二,第三個參數(shù),開頭的例子沒有第三個參數(shù),因此沒有iterator2屬性。
對于v-if指令,把v-if指令中綁定的內(nèi)容取出放在if中,與此同時初始化ifConditions屬性為數(shù)組,然后往里面存放對象:{exp,block},其中exp存放v-if指令中綁定的內(nèi)容,block指向el。
optimize過程在此不做分析,因為本例子沒有靜態(tài)節(jié)點。
上一篇文章從const code = generate(ast, options)開始分析過其生成代碼的過程,generate內(nèi)部會調(diào)用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優(yōu)先級比v-if高, // 因為解析ast樹生成渲染函數(shù)代碼時,會先解析ast樹中涉及到v-for的屬性 // 然后再解析ast樹中涉及到v-if的屬性 // 而且genFor在會把el.forProcessed置為true,防止重復(fù)解析v-for相關(guān)屬性 } 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的函數(shù)源碼:
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)}` + //遞歸調(diào)用genElement "})"}在我們的例子里,當(dāng)他處理li的ast樹時,會先調(diào)用genElement,處理到for屬性時,此時forProcessed為虛值,此時調(diào)用genFor處理li樹中的v-for相關(guān)的屬性。然后再調(diào)用genElement處理li樹,此時因為forProcessed在genFor中已被標(biāo)記為true。因此genFor不會被執(zhí)行,繼而執(zhí)行genIf處理與v-if相關(guān)的屬性。
export function genIf (el,state,altGen,altEmpty) { el.ifProcessed = true // avoid recursion // 調(diào)用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) }}參考 前端進(jìn)階面試題詳細(xì)解答
最后,經(jīng)過codegen生成的js代碼如下:
function render() { with(this) { return _c("ul", _l((items), function (item, index) { return (index !== 0) ? _c("li") : _e() }), 0) }}其中:
_c: 調(diào)用 createElement去創(chuàng)建 VNode
_l: renderList函數(shù),主要用來渲染列表
_e: createEmptyVNode函數(shù),主要用來創(chuàng)建空VNode
為什么v-for的優(yōu)先級比v-if的高?總結(jié)來說是編譯有三個過程,parse->optimize->codegen。在codegen過程中,會先解析AST樹中的與v-for相關(guān)的屬性,再解析與v-if相關(guān)的屬性。除此之外,也可以知道Vue對v-for和v-if是怎么處理的。
(學(xué)習(xí)視頻分享:vuejs入門教程、編程基礎(chǔ)視頻)
以上就是vue優(yōu)先級比較:為什么v-for比v-if高的詳細(xì)內(nèi)容,更多請關(guān)注php中文網(wǎng)其它相關(guān)文章!
關(guān)鍵詞: