>웹 프론트엔드 >View.js >Vue 우선순위 비교: v-for가 v-if보다 높은 이유

Vue 우선순위 비교: v-for가 v-if보다 높은 이유

青灯夜游
青灯夜游앞으로
2023-03-14 19:44:191675검색

vue에서 v-if보다 v-for의 우선순위가 더 높은 이유는 무엇인가요? 다음 기사에서는 소스 코드를 분석하여 이 질문에 답할 것입니다. 모든 사람에게 도움이 되기를 바랍니다.

Vue 우선순위 비교: v-for가 v-if보다 높은 이유

때때로 우리는 v-for 또는 v-if 중 누가 더 높은 우선순위를 갖는지 묻는 경우가 많습니다. 여기서는 소스를 분석하여 이 질문에 답하겠습니다. 코드. v-forv-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函数

我们再次顺着这三个步骤对上述例子进行分析。

parse

parse过程中,会对模板使用大量的正则表达式去进行解析。开头的例子会被解析成以下AST节点:

// 其实ast有很多属性,我这里只展示涉及到分析的属性
ast = {
  &#39;type&#39;: 1,
  &#39;tag&#39;: &#39;ul&#39;,
  &#39;attrsList&#39;: [],
  attrsMap: {},
  &#39;children&#39;: [{
    &#39;type&#39;: 1,
    &#39;tag&#39;: &#39;li&#39;,
    &#39;attrsList&#39;: [],
    &#39;attrsMap&#39;: {
      &#39;v-for&#39;: &#39;(item,index) in data&#39;,
      &#39;v-if&#39;: &#39;index!==0&#39;
     },
     // v-if解析出来的属性
    &#39;if&#39;: &#39;index!==0&#39;,
    &#39;ifConditions&#39;: [{
      &#39;exp&#39;: &#39;index!==0&#39;,
      &#39;block&#39;: // 指向el自身
    }],
    // v-for解析出来的属性
    &#39;for&#39;: &#39;items&#39;,
    &#39;alias&#39;: &#39;item&#39;,
    &#39;iterator1&#39;: &#39;index&#39;,

    &#39;parent&#39;: // 指向其父节点
    &#39;children&#39;: [
      &#39;type&#39;: 2,
      &#39;expression&#39;: &#39;_s(item)&#39;
      &#39;text&#39;: &#39;{{item}}&#39;,
      &#39;tokens&#39;: [
        {&#39;@binding&#39;:&#39;item&#39;},
      ]
    ]
  }]
}

对于v-for指令,除了记录在attrsMapattrsList,还会新增for(对应要遍历的对象或数组),aliasiterator1,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 === &#39;template&#39; && !el.slotTarget && !state.pre) {
    return genChildren(el, state) || &#39;void 0&#39;
  } else if (el.tag === &#39;slot&#39;) {
    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(&#39;${el.tag}&#39;${        data ? `,${data}` : &#39;&#39; // data      }${        children ? `,${children}` : &#39;&#39; // children      })`
    }
    // module transforms
    for (let i = 0; i < state.transforms.length; i++) {
      code = state.transforms[i](el, code)
    }
    return code
  }
}

接下来依次看看genForgenIf的函数源码:

export function genFor (el, state , altGen, altHelper) {
  const exp = el.for
  const alias = el.alias
  const iterator1 = el.iterator1 ? `,${el.iterator1}` : &#39;&#39;
  const iterator2 = el.iterator2 ? `,${el.iterator2}` : &#39;&#39;

  el.forProcessed = true // avoid recursion
  return `${altHelper || &#39;_l&#39;}((${exp}),` + 
    `function(${alias}${iterator1}${iterator2}){` +
      `return ${(altGen || genElement)(el, state)}` + //递归调用genElement
    &#39;})&#39;
}

在我们的例子里,当他处理liast树时,会先调用genElement,处理到for属性时,此时forProcessed为虚值,此时调用genFor处理li树中的v-for相关的属性。然后再调用genElement处理li树,此时因为forProcessedgenFor中已被标记为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 || &#39;_e()&#39; // _e用于生成空VNode
  }

  const condition = conditions.shift()
  if (condition.exp) { //condition.exp即v-if绑定值,例子中则为&#39;index!==0&#39;
    // 生成一段带三目运算符的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(&#39;ul&#39;, _l((items), function (item, index) {
      return (index !== 0) ? _c(&#39;li&#39;) : _e()
    }), 0)
  }
}

其中:

  • _c: 调用 createElement 去创建 VNode

  • _l: renderList函数,主要用来渲染列表

  • _e: createEmptyVNode函数,主要用来创建空VNode

总结

为什么v-for的优先级比v-if的高?总结来说是编译有三个过程,parse->optimize->codegen。在codegen过程中,会先解析AST树中的与v-for相关的属性,再解析与v-if相关的属性。除此之外,也可以知道Vuev-forv-if

다음 내용은 v-model에 대해 이야기할 때, 무엇을 말하는 걸까요?를 기반으로 분석되었으므로, 다음 내용을 읽기 전에 이 글을 먼저 읽으시기 바랍니다.

계속해서 컴파일을 시작하세요

다음 예를 들어 분석을 시작하세요. rrreee이전 글에서 알 수 있듯이, 컴파일에는 세 가지 단계가 있습니다.
  • 파싱: 템플릿 문자열을 구문 분석하여 AST 구문 트리를 생성합니다.
  • 최적화: 구문을 최적화합니다. 주로 정적 노드를 표시하는 트리. 업데이트된 페이지의 성능을 향상합니다. [관련 권장사항: vuejs 비디오 튜토리얼, 웹 프론트엔드 개발🎜]
  • codegen: 주로 render 함수와 staticRenderFns 함수인 js 코드를 생성합니다.
🎜이 세 단계를 다시 수행하여 위의 예를 분석하겠습니다. 🎜

구문 분석

🎜구문 분석이 과정에서 수많은 정규 표현식을 사용하여 주형. 시작 부분의 예는 다음 AST 노드로 구문 분석됩니다. 🎜rrreee🎜attrsMap의 레코드 외에 v-for 지시문의 경우 > 및 attrsList , for(탐색할 객체 또는 배열에 해당), alias, iterator1도 추가합니다. code>, <code>iterator2v-for 명령어 바인딩 내용의 첫 번째, 두 번째, 세 번째 매개변수에 해당합니다. 시작 부분의 예에는 세 번째 매개변수가 없습니다. 이므로 iterator2 속성이 없습니다. 🎜🎜v-if 명령의 경우 v-if 명령에 바인딩된 내용을 꺼내 if에 넣고 동시에 초기화ifConditions 속성은 배열이고 객체는 {exp,block}에 저장됩니다. 여기서 exp는 v-if 명령어에 바인딩된 콘텐츠인 blockel을 가리킵니다. 🎜🎜최적화 이 예에는 정적 노드가 없기 때문에 여기서 프로세스는 분석되지 않습니다. 🎜

codegen

🎜이전 글은 const code = generate(ast, options)부터 분석했습니다. 코드 생성 프로세스에서 generate는 내부적으로 genElement를 호출하여 AST 구문 트리인 el을 구문 분석합니다. genElement의 소스 코드를 살펴보겠습니다. 🎜rrreee🎜 다음으로 genForgenIf의 함수 소스 코드를 살펴보겠습니다. 🎜rrreee🎜예제에서 그는 liast 트리를 처리할 때 먼저 genElement를 호출한 다음 를 처리합니다. >for 속성. 이때 forProcessed는 가상 값입니다. 이때 v-for를 처리하려면 genFor를 호출하세요. > li 트리 속성과 관련되어 있습니다. 그런 다음 genElement를 호출하여 li 트리를 처리합니다. forProcessedgenFor true로 표시되었기 때문입니다. /코드>. 따라서 genFor는 실행되지 않고 genIf가 실행되어 v-if에 관련된 속성을 처리하게 됩니다. 🎜rrreee🎜참조프런트엔드 고급 면접 질문에 대한 자세한 답변🎜🎜🎜마지막으로 codegen으로 생성된 js 코드는 다음과 같습니다. 🎜rrreee🎜그 중: 🎜
  • 🎜_c: createElement를 호출하여 VNode🎜
  • 🎜 생성 _l : renderList 함수, 주로 목록을 렌더링하는 데 사용됨🎜
  • 🎜_e: createEmptyVNode 함수, 주로 빈VNode🎜

요약

🎜v-for가 v보다 우선순위가 높은 이유는 무엇입니까? -만약에? 요약하면, 컴파일에는 파싱 ->최적화 ->codegen이라는 세 가지 프로세스가 있습니다. codegen 프로세스에서는 AST 트리의 v-for와 관련된 속성을 먼저 구문 분석한 다음 와 관련된 속성을 구문 분석합니다. >v-if 관련 속성. 또한 Vuev-forv-if를 처리하는 방법도 알 수 있습니다. 🎜🎜 (학습 영상 공유: 🎜vuejs 입문 튜토리얼🎜, 🎜기본 프로그래밍 영상🎜)🎜

위 내용은 Vue 우선순위 비교: v-for가 v-if보다 높은 이유의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!

성명:
이 기사는 juejin.cn에서 복제됩니다. 침해가 있는 경우 admin@php.cn으로 문의하시기 바랍니다. 삭제