vue為什么v-for的優(yōu)先級(jí)比v-if高原理解析
前言
有時(shí)候有些面試中經(jīng)常會(huì)問(wèn)到v-for與v-if誰(shuí)的優(yōu)先級(jí)高,這里就通過(guò)分析源碼去解答一下這個(gè)問(wèn)題。
下面的內(nèi)容是在 當(dāng)我們談及v-model,我們?cè)谟懻撌裁?的基礎(chǔ)上分析的,所以閱讀下面內(nèi)容之前可先看這篇文章。
繼續(xù)從編譯出發(fā)
以下面的例子出發(fā)分析:
new Vue({
el:'#app',
template:`
<ul>
<li v-for="(item,index) in items" v-if="index!==0">
{{item}}
</li>
</ul>
`
})從上篇文章可以知道,編譯有三個(gè)步驟
- parse : 解析模板字符串生成 AST語(yǔ)法樹(shù)
- optimize : 優(yōu)化語(yǔ)法樹(shù),主要時(shí)標(biāo)記靜態(tài)節(jié)點(diǎn),提高更新頁(yè)面的性能
- codegen : 生成js代碼,主要是render函數(shù)和staticRenderFns函數(shù)
我們?cè)俅雾樦@三個(gè)步驟對(duì)上述例子進(jìn)行分析。
parse
parse過(guò)程中,會(huì)對(duì)模板使用大量的正則表達(dá)式去進(jìn)行解析。開(kāi)頭的例子會(huì)被解析成以下AST節(jié)點(diǎn):
// 其實(shí)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解析出來(lái)的屬性
'if': 'index!==0',
'ifConditions': [{
'exp': 'index!==0',
'block': // 指向el自身
}],
// v-for解析出來(lái)的屬性
'for': 'items',
'alias': 'item',
'iterator1': 'index',
'parent': // 指向其父節(jié)點(diǎn)
'children': [
'type': 2,
'expression': '_s(item)'
'text': '{{item}}',
'tokens': [
{'@binding':'item'},
]
]
}]
}對(duì)于v-for指令,除了記錄在attrsMap和attrsList,還會(huì)新增for(對(duì)應(yīng)要遍歷的對(duì)象或數(shù)組),alias,iterator1,iterator2對(duì)應(yīng)v-for指令綁定內(nèi)容中的第一,第二,第三個(gè)參數(shù),開(kāi)頭的例子沒(méi)有第三個(gè)參數(shù),因此沒(méi)有iterator2屬性。
對(duì)于v-if指令,把v-if指令中綁定的內(nèi)容取出放在if中,與此同時(shí)初始化ifConditions屬性為數(shù)組,然后往里面存放對(duì)象:{exp,block},其中exp存放v-if指令中綁定的內(nèi)容,block指向el。
optimize 過(guò)程在此不做分析,因?yàn)楸纠記](méi)有靜態(tài)節(jié)點(diǎn)。
codegen
上一篇文章從const code = generate(ast, options)開(kāi)始分析過(guò)其生成代碼的過(guò)程,generate內(nèi)部會(huì)調(diào)用genElement用來(lái)解析el,也就是AST語(yǔ)法樹(shù)。我們來(lái)看一下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)
// 其實(shí)從此處可以初步知道為什么v-for優(yōu)先級(jí)比v-if高,
// 因?yàn)榻馕鯽st樹(shù)生成渲染函數(shù)代碼時(shí),會(huì)先解析ast樹(shù)中涉及到v-for的屬性
// 然后再解析ast樹(shù)中涉及到v-if的屬性
// 而且genFor在會(huì)把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
}
}接下來(lái)依次看看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樹(shù)時(shí),會(huì)先調(diào)用genElement,處理到for屬性時(shí),此時(shí)forProcessed為虛值,此時(shí)調(diào)用genFor處理li樹(shù)中的v-for相關(guān)的屬性。然后再調(diào)用genElement處理li樹(shù),此時(shí)因?yàn)?code>forProcessed在genFor中已被標(biāo)記為true。因此genFor不會(huì)被執(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'
// 生成一段帶三目運(yùn)算符的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īng)過(guò)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ù),主要用來(lái)渲染列表_e:createEmptyVNode函數(shù),主要用來(lái)創(chuàng)建空VNode
總結(jié)
為什么v-for的優(yōu)先級(jí)比v-if的高?總結(jié)來(lái)說(shuō)是編譯有三個(gè)過(guò)程,parse->optimize->codegen。在codegen過(guò)程中,會(huì)先解析AST樹(shù)中的與v-for相關(guān)的屬性,再解析與v-if相關(guān)的屬性。除此之外,也可以知道Vue對(duì)v-for和v-if是怎么處理的。
以上就是vue為什么v-for的優(yōu)先級(jí)比v-if高原理解析的詳細(xì)內(nèi)容,更多關(guān)于vue優(yōu)先級(jí)v-for v-if的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
vue自定義指令添加跟隨鼠標(biāo)光標(biāo)提示框v-tooltip方式
這篇文章主要介紹了vue自定義指令添加跟隨鼠標(biāo)光標(biāo)提示框v-tooltip方式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2022-10-10
Vue3中Provide?/?Inject的實(shí)現(xiàn)原理分享
provide和inject主要為高階插件/組件庫(kù)提供用例,這篇文章主要給大家介紹了關(guān)于Vue3中Provide?/?Inject的實(shí)現(xiàn)原理,文中通過(guò)實(shí)例代碼介紹的非常詳細(xì),需要的朋友可以參考下2022-02-02
vue2.0 和 animate.css的結(jié)合使用
animate.css是一款前端動(dòng)畫(huà)庫(kù),相似的有velocity-animate。這篇文章給大家介紹vue2.0 和 animate.css的結(jié)合使用,需要的朋友參考下吧2017-12-12
vue+jquery+lodash實(shí)現(xiàn)滑動(dòng)時(shí)頂部懸浮固定效果
這篇文章主要為大家詳細(xì)介紹了vue+jquery+lodash實(shí)現(xiàn)滑動(dòng)時(shí)頂部懸浮固定效果,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2018-04-04
Vue中JS動(dòng)畫(huà)與Velocity.js的結(jié)合使用
這篇文章主要介紹了Vue中JS動(dòng)畫(huà)與Velocity.js的結(jié)合使用,小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2019-02-02
vue中echarts關(guān)系圖動(dòng)態(tài)增刪節(jié)點(diǎn)以及連線方式
這篇文章主要介紹了vue中echarts關(guān)系圖動(dòng)態(tài)增刪節(jié)點(diǎn)以及連線方式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2022-07-07

