本篇內容介紹了“vue中v-for比v-if高的原因是什么”的有關知識,在實際案例的操作過程中,不少人都會遇到這樣的困境,接下來就讓小編帶領大家學習一下如何處理這些情況吧!希望大家仔細閱讀,能夠學有所成!
以下面的例子出發分析:
new Vue({
el:'#app',
template:`
<ul>
<li v-for="(item,index) in items" v-if="index!==0">
{{item}}
</li>
</ul>
`
})
從上篇文章可以知道,編譯有三個步驟
parse : 解析模板字符串生成 AST語法樹
optimize : 優化語法樹,主要時標記靜態節點,提高更新頁面的性能
codegen : 生成js代碼,主要是render函數和staticRenderFns函數
我們再次順著這三個步驟對上述例子進行分析。
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
“vue中v-for比v-if高的原因是什么”的內容就介紹到這里了,感謝大家的閱讀。如果想了解更多行業相關的知識可以關注億速云網站,小編將為大家輸出更多高質量的實用文章!
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。