vue에서 v-if보다 v-for의 우선순위가 더 높은 이유는 무엇인가요? 다음 기사에서는 소스 코드를 분석하여 이 질문에 답할 것입니다. 모든 사람에게 도움이 되기를 바랍니다.
때때로 우리는 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
节点:
// 其实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
为什么v-for的优先级比v-if的高?总结来说是编译有三个过程,parse
->optimize
->codegen
。在codegen
过程中,会先解析AST
树中的与v-for
相关的属性,再解析与v-if
相关的属性。除此之外,也可以知道Vue
对v-for
和v-if
AST
노드로 구문 분석됩니다. 🎜rrreee🎜attrsMap
의 레코드 외에 v-for
지시문의 경우 > 및 attrsList
, for
(탐색할 객체 또는 배열에 해당), alias
, iterator1도 추가합니다. code>, <code>iterator2
는 v-for
명령어 바인딩 내용의 첫 번째, 두 번째, 세 번째 매개변수에 해당합니다. 시작 부분의 예에는 세 번째 매개변수가 없습니다. 이므로 iterator2
속성이 없습니다. 🎜🎜v-if
명령의 경우 v-if
명령에 바인딩된 내용을 꺼내 if
에 넣고 동시에 초기화ifConditions 속성은 배열이고 객체는 {exp,block}
에 저장됩니다. 여기서 exp
는 v-if 명령어에 바인딩된 콘텐츠인 block
은 el
을 가리킵니다. 🎜🎜최적화 이 예에는 정적 노드가 없기 때문에 여기서 프로세스는 분석되지 않습니다. 🎜const code = generate(ast, options)
부터 분석했습니다. 코드 생성 프로세스에서 generate
는 내부적으로 genElement
를 호출하여 AST
구문 트리인 el
을 구문 분석합니다. genElement
의 소스 코드를 살펴보겠습니다. 🎜rrreee🎜 다음으로 genFor
및 genIf
의 함수 소스 코드를 살펴보겠습니다. 🎜rrreee🎜예제에서 그는 li
의 ast
트리를 처리할 때 먼저 genElement
를 호출한 다음 를 처리합니다. >for
속성. 이때 forProcessed
는 가상 값입니다. 이때 v-for
를 처리하려면 genFor
를 호출하세요. > li
트리 속성과 관련되어 있습니다. 그런 다음 genElement
를 호출하여 li
트리를 처리합니다. forProcessed
가 genFor
true로 표시되었기 때문입니다. /코드>. 따라서 genFor
는 실행되지 않고 genIf
가 실행되어 v-if
에 관련된 속성을 처리하게 됩니다. 🎜rrreee🎜참조프런트엔드 고급 면접 질문에 대한 자세한 답변🎜🎜🎜마지막으로 codegen으로 생성된 js 코드는 다음과 같습니다. 🎜rrreee🎜그 중: 🎜_c
: createElement
를 호출하여 VNode
🎜 생성 _l
: renderList
함수, 주로 목록을 렌더링하는 데 사용됨🎜_e
: createEmptyVNode
함수, 주로 빈VNode🎜파싱
->최적화
->codegen
이라는 세 가지 프로세스가 있습니다. codegen
프로세스에서는 AST
트리의 v-for
와 관련된 속성을 먼저 구문 분석한 다음 와 관련된 속성을 구문 분석합니다. >v-if
관련 속성. 또한 Vue
가 v-for
및 v-if
를 처리하는 방법도 알 수 있습니다. 🎜🎜 (학습 영상 공유: 🎜vuejs 입문 튜토리얼🎜, 🎜기본 프로그래밍 영상🎜)🎜위 내용은 Vue 우선순위 비교: v-for가 v-if보다 높은 이유의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!