v-for
和v-if
的優先順序,相信你看完後會茅塞頓開的。如何快速入門VUE3.0:進入學習
1、本身並不建議將v-for和v-if同時使用的。(學習視訊分享:)
2、vue2裡面v-for比v-if的優先順序更高。因為vue2在模板編譯的時候會先處理v-for再處理v-if,所以生成的渲染函數會先執行迴圈,然後在迴圈裡面再執行條件判斷。
3、這樣做帶來的問題就是
對於場景1:<li v-for="user in users" v-if="user.show">
每次重新渲染的時候,都要重新遍歷整個列表,其實我們只需要列表的一部分,這樣做浪費效能。推薦的做法是,通過計算屬性先過濾出我們需要的部分,再去渲染,更高效。
對於場景2: <li v-for="user in users" v-if="globalShow">
globalShow這個判斷其實如果是false,迴圈並不需要執行,但是現在跟v-if一起用,不管globalShow是否是true都要執行迴圈,完全是浪費。推薦的做法是將v-if上移到ul容器。
4、需要注意的是,vue3的breaking change,在vue3中v-if的優先順序比v-for高,所以如果同時使用的話,對於場景1,這個時候user還沒有,v-if="user.show"就會報錯
5、一般我們如果有用eslint,也會給我們報錯,對應的規則是:vue/no-use-v-if-with-v-for
例如:以下的模板,將會生成下面的渲染函數
<ul> <li v-for="user in users" v-if="user.isActive" :key="user.id"> {{ user.name }} </li> </ul>
生成的渲染函數如下
with(this) { return _c('ul', _l((users), function (user) { return (user.isActive) ? _c('li', user.name) : _e() }), 0) }
從上面生成的渲染函數可以看出,會先執行_l
遍歷user
,在裡面進行條件判斷
處理v-if和v-for的原始碼
src/compiler/index.js
// 模板解析,生成ast樹 const ast = parse(template.trim(), options) if (options.optimize !== false) { optimize(ast, options) } const code = generate(ast, options)
根據ast生成程式碼,假如是上面的模板,生成的ast簡化後如下
// 可以看出v-for和v-if都解析出來了 ast = { 'type': 1, 'tag': 'ul', 'attrsList': [], 'attrsMap': {}, 'children': [{ 'type': 1, 'tag': 'li', 'attrsList': [], 'attrsMap': { 'v-for': 'user in users', 'v-if': 'user.show' }, // v-if解析出來的屬性 'if': 'user.show', 'ifConditions': [{ 'exp': 'user.show', 'block': // 指向el自身 }], // v-for解析出來的屬性 'for': 'users', 'alias': 'user', 'iterator1': 'index', 'parent': // 指向其父節點 'children': [ 'type': 2, 'expression': '_s(user)' 'text': '{{user}}', 'tokens': [ {'@binding':'user'}, ] ] }] }
compiler/codegen/index.js
// generate 呼叫 genElement const code = ast ? genElement(ast, state) : '_c("div")' // genElement裡面的處理 if (el.staticRoot && !el.staticProcessed) { return genStatic(el, state) } else if (el.once && !el.onceProcessed) { return genOnce(el, state) // 從這可以看出來,先執行genFor,處理v-for指令,在genFor裡面會遞迴呼叫genElement,繼續處理v-if,genFor會將forProcessed設為true,這樣下次進來的時候就不會處理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 { // 最後這裡處理標籤等 const children = el.inlineTemplate ? null : genChildren(el, state, true) code = `_c('${el.tag}'${ data ? `,${data}` : '' // data }${ children ? `,${children}` : '' // children })` } // genFor的程式碼 const exp = el.for // 對應上面ast的 for: users const alias = el.alias // alias: user // iterator1 對應v-for的(item,key,index) in items的key // iterator2 對應的是index // 通常我們遍歷陣列 key就是index // 假如我們遍歷的是物件 key就是物件的key,index就是遍歷的索引 const iterator1 = el.iterator1 ? `,${el.iterator1}` : '' const iterator2 = el.iterator2 ? `,${el.iterator2}` : '' el.forProcessed = true // 下次遞迴呼叫genElement的時候就不會重複處理v-for了 return `${altHelper || '_l'}((${exp}),` + `function(${alias}${iterator1}${iterator2}){` // 這裡處理完了v-for,遞迴呼叫genElement繼續處理v-if `return ${(altGen || genElement)(el, state)}` + '})'
最終會生成類似如下的程式碼返回出去
_l((users), function(user, index) { // 如果有v-if 前面就會有個條件判斷,如user.isActive return (user.isActive) ? _c('li', user.name) : _e() });
(學習視訊分享:、)
以上就是優先順序比較:v-for 和 v-if哪個更高?的詳細內容,更多請關注TW511.COM其它相關文章!