最新公告
  • 欢迎您光临起源地模板网,本站秉承服务宗旨 履行“站长”责任,销售只是起点 服务永无止境!立即加入钻石VIP
  • [Vue源码]为什么v-for的优先级比v-if的高?

    正文概述 掘金(村上小树)   2021-03-21   612

    前言

    有时候有些面试中经常会问到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 : 优化语法树,主要时标记静态节点,提高更新页面的性能
    • codegen : 生成js代码,主要是render函数和staticRenderFns函数

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

    parse

    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指令,除了记录在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 === '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
      }
    }
    

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

    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
        '})'
    }
    

    在我们的例子里,当他处理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 || '_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)
      }
    }
    

    其中:

    1. _c: 调用 createElement 去创建 VNode
    2. _l: renderList函数,主要用来渲染列表
    3. _e: createEmptyVNode函数,主要用来创建空VNode

    总结

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


    起源地下载网 » [Vue源码]为什么v-for的优先级比v-if的高?

    常见问题FAQ

    免费下载或者VIP会员专享资源能否直接商用?
    本站所有资源版权均属于原作者所有,这里所提供资源均只能用于参考学习用,请勿直接商用。若由于商用引起版权纠纷,一切责任均由使用者承担。更多说明请参考 VIP介绍。
    提示下载完但解压或打开不了?
    最常见的情况是下载不完整: 可对比下载完压缩包的与网盘上的容量,若小于网盘提示的容量则是这个原因。这是浏览器下载的bug,建议用百度网盘软件或迅雷下载。若排除这种情况,可在对应资源底部留言,或 联络我们.。
    找不到素材资源介绍文章里的示例图片?
    对于PPT,KEY,Mockups,APP,网页模版等类型的素材,文章内用于介绍的图片通常并不包含在对应可供下载素材包内。这些相关商业图片需另外购买,且本站不负责(也没有办法)找到出处。 同样地一些字体文件也是这种情况,但部分素材会在素材包内有一份字体下载链接清单。
    模板不会安装或需要功能定制以及二次开发?
    请QQ联系我们

    发表评论

    还没有评论,快来抢沙发吧!

    如需帝国cms功能定制以及二次开发请联系我们

    联系作者

    请选择支付方式

    ×
    迅虎支付宝
    迅虎微信
    支付宝当面付
    余额支付
    ×
    微信扫码支付 0 元