vue为什么v-for的优先级比v-if的高?下面本篇文章就通过分析源码去解答一下这个问题,希望对大家有所帮助!
有时候有些面试中经常会问到v-for与v-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函数我们再次顺着这三个步骤对上述例子进行分析。
parseparse过程中,会对模板使用大量的正则表达式去进行解析。开头的例子会被解析成以下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 过程在此不做分析,因为本例子没有静态节点。
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 === '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
总结为什么v-for的优先级比v-if的高?总结来说是编译有三个过程,parse->optimize->codegen。在codegen过程中,会先解析ast树中的与v-for相关的属性,再解析与v-if相关的属性。除此之外,也可以知道vue对v-for和v-if是怎么处理的。
(学习视频分享:vuejs入门教程、编程基础视频)
以上就是vue优先级比较:为什么v-for比v-if高的详细内容。
