首页 > 其他分享 >vue为什么v-for的优先级比v-if的高?

vue为什么v-for的优先级比v-if的高?

时间:2023-02-14 10:13:20浏览次数:67  
标签:为什么 vue return el state code 优先级 解析 属性

前言

有时候有些面试中经常会问到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,return,el,state,code,优先级,解析,属性
From: https://www.cnblogs.com/bbxiaxia1998/p/17118747.html

相关文章

  • 前端一面必会vue面试题总结
    Vue模板编译原理Vue的编译过程就是将template转化为render函数的过程分为以下三步第一步是将模板字符串转换成elementASTs(解析器)第二步是对AST进行静态节......
  • new Vue的时候到底做了什么
    Vue加载流程1.初始化的第一阶段是Vue实例也就是vm对象创建前后:首先Vue进行生命周期,事件初始化发生在beforeCreate生命周期函数前,然后进行数据监测和数据代理的初始化,也就......
  • 前端二面经典vue面试题总结
    Vue加载流程1.初始化的第一阶段是Vue实例也就是vm对象创建前后:首先Vue进行生命周期,事件初始化发生在beforeCreate生命周期函数前,然后进行数据监测和数据代理的初始化,也就......
  • 转载:Vue+springboot集成PageOffice实现在线编辑Word、excel文档
    说明:   PageOffice是一款在线的office编辑软件,帮助Web应用系统或Web网站实现用户在线编辑Word、Excel、PowerPoint文档。可以完美实现在线公文流转,领导批阅,盖章。......
  • 我为什么要放弃 RESTful,选择拥抱 GraphQL 转载
    REST作为一种现代网络应用非常流行的软件架构风格,自从RoyFielding博士在2000年他的博士论文中提出来到现在已经有了20年的历史。它的简单易用性,可扩展性,伸缩性受到广大Web......
  • vue3watch
    vue3watch监视不到对象里面的oldValueref定义的基本类型响应式数据可以监视ref定义的多个基本类型响应式数据watch([name,age],(newV,oldV)=>{console.lo......
  • Vue的v-html指令说明,含案例
    v-html指令总结:1、作用:向指定节点中渲染包含html结构的内容2、与插值语法的区别:(1)v-html会替换掉节点中所有的内容,{{}}则不会(2)v-html可以识别html结构3、特别注意:(1......
  • vue原理:diff、模板编译、渲染过程等
    一、虚拟DOM:因为DOM操作非常消耗性能,在操作DOM时,会出现DOM的回流(Reflow:元素大小或者位置发生改变)与重绘(元素样式的改变)使DOM重新渲染。现在的框架Vue和React很少直接操作......
  • #程序猿在做项目经理:今日思考|为什么我要来cnblogs
    看到此贴的各位老板技术实力大部分应该是比我强来此,一方面向各位技术老板学习另外一方面,我也想分享一些我转行做项目的一些思考和经历我的理念是:独乐乐,不如众乐乐或许......
  • 前端发展史 vue介绍 vue的快速使用
    目录前端发展历史vue介绍vue快速使用之helloworld前端发展历史#1HTML(5)、CSS(3)、JavaScript(ES5、ES6):编写一个个的页面->给后端(PHP、Python、Go、Java)->后端......