Loading [MathJax]/jax/output/CommonHTML/config.js
前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
社区首页 >专栏 >vue为什么v-for的优先级比v-if的高?

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

原创
作者头像
bb_xiaxia1998
发布于 2022-10-19 02:22:29
发布于 2022-10-19 02:22:29
28400
代码可运行
举报
运行总次数:0
代码可运行

前言

有时候有些面试中经常会问到v-forv-if谁的优先级高,这里就通过分析源码去解答一下这个问题。

下面的内容是在 当我们谈及v-model,我们在讨论什么?的基础上分析的,所以阅读下面内容之前可先看这篇文章。

继续从编译出发

以下面的例子出发分析:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
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节点:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
// 其实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 过程在此不做分析,因为本例子没有静态节点。

参考vue面试题解答 前端vue面试题详细解答

codegen

上一篇文章从const code = generate(ast, options)开始分析过其生成代码的过程,generate内部会调用genElement用来解析el,也就是AST语法树。我们来看一下genElement的源码:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
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的函数源码:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
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相关的属性。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
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代码如下:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
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是怎么处理的。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
暂无评论
推荐阅读
编辑精选文章
换一批
【Vue原理】Compile - 源码版 之 generate 节点拼接
终于走到了 Vue 渲染三巨头的最后一步了,那就是 generate,反正文章已经写完了,干脆早点发完,反正这部分内容大家也不会马上看哈哈
神仙朱
2019/08/02
8840
【Vue原理】Compile - 源码版 之 generate 节点拼接
[咖聊] “模板编译”真经
冲一杯美式 ☕️ ,读编译真经,岂不快哉? 本文的 🍪 (表示 例子,☕️ 和 🍪 更配哦!全文都会围绕这个 DEMO 做解析。⚠️ 因不能直接跳转到外链,注意有标注的地方,文末有对应的地址哦,跳转着看更容易理解哦!): <div id="app"> <!-- 这是一个注释节点 --> <Child name="yjc" :age="12" v-if="isShow"></Child> <input type="text" v-model="inputValue" /> <d
码农小余
2022/06/16
1K0
[咖聊] “模板编译”真经
面试官:为什么Vue中的v-if和v-for不建议一起用?
v-if 指令用于条件性地渲染一块内容。这块内容只会在指令的表达式返回 true值的时候被渲染
@超人
2021/02/26
9640
「.vue文件的编译」3. 模板编译之AST生成
下面parseHTML方法是用来遍历html字符串的并解析出标签(当然包含标签中的属性)、文本等信息,详细分析参考这里。
tinyant
2023/02/24
1.2K0
编译optimize源码实现过程
当我们的模板 template 经过 parse 过程后,会输出生成 AST 树,那么接下来我们需要对这颗树做优化,optimize 的逻辑是远简单于 parse 的逻辑,所以理解起来会轻松很多。 为什么要有优化过程,因为我们知道 Vue 是数据驱动,是响应式的,但是我们的模板并不是所有数据都是响应式的,也有很多数据是首次渲染后就永远不会变化的,那么这部分数据生成的 DOM 也不会变化,我们可以在 patch 的过程跳过对他们的比对。 来看一下 optimize 方法的定义,在 src/compiler/optimizer.js 中:
PHP开发工程师
2022/08/04
3040
【Vue原理】Compile - 源码版 之 属性解析
哈哈哈,今天终于到了属性解析的部分了,之前已经讲过了 parse 流程,标签解析,最后就只剩下 属性解析了 (´・ᴗ・`)
神仙朱
2019/08/02
9860
【Vue原理】Compile - 源码版 之 属性解析
Vue2.0模板编译原理
写过 Vue 的同学肯定体验过, .vue 这种单文件组件有多么方便。但是我们也知道,Vue 底层是通过虚拟 DOM 来进行渲染的,那么 .vue 文件的模板到底是怎么转换成虚拟 DOM 的呢?这一块对我来说一直是个黑盒,之前也没有深入研究过,今天打算一探究竟。
若川
2020/11/10
1.2K0
Vue2.0模板编译原理
Vue虚拟dom是如何被创建的
我们看到了AST经过三轮加工,最终脱胎换骨成为render function code。那么我们这一章就来讲AST的第三次试炼。入口文件./codegen/index
yyds2026
2022/10/21
5350
深入理解 Vue 模板渲染:Vue 模板反编译
熟悉 vue 的同学应该都知道,vue 单文件模板中一般含有三个部分,template,script,style。
WecTeam
2020/09/01
7K1
深入理解 Vue 模板渲染:Vue 模板反编译
Vue模板是怎样编译的
这一章我们开始讲模板解析编译:总结来说就是通过compile函数把tamplate解析成render Function形式的字符串compiler/index.js
yyds2026
2022/10/19
1K0
# Vue 模板编译原理解析
在 Vue 开发过程中,我们通常使用.vue文件进行开发,然后上线时打包成一个js最后在页面中加载然后渲染 DOM。
九旬
2023/10/18
3790
# Vue 模板编译原理解析
前端常见vue面试题(必备)_2023-03-01
模板指令的代码都会生成在render函数中,通过app.$options.render就能得到渲染函数
用户10358241
2023/03/01
8640
Vue2剥丝抽茧-模版编译之静态render
上篇文章 模版编译之生成AST 中将模版转为了 AST ,这篇文章会将 AST 转为最终的 render 函数。
windliang
2022/12/21
2870
Vue2剥丝抽茧-模版编译之静态render
前端高频vue面试题总结3
通过webpack的tree-shaking功能,可以将无用模块“剪辑”,仅打包需要的
bb_xiaxia1998
2023/01/05
1.2K0
这可能是你需要的vue考点梳理
Vue2.x开始引入"Virtual DOM",消除了和React在这方面的差异,但是在具体的细节还是有各自的特点。
bb_xiaxia1998
2022/11/01
1.1K0
Vue事件绑定原理
Vue中通过v-on或其语法糖@指令来给元素绑定事件并且提供了事件修饰符,基本流程是进行模板编译生成AST,生成render函数后并执行得到VNode,VNode生成真实DOM节点或者组件时候使用addEventListener方法进行事件绑定。
WindRunnerMax
2020/09/10
8.8K0
vue面试题总结(持续更新中)
vue-router中两个重要组件router-link和router-view,分别起到导航作用和内容渲染作用,但是回答如何生效还真有一定难度
bb_xiaxia1998
2022/12/12
1.5K0
「.vue文件的编译」4. 模板编译之AST的优化
如果这个节点是一个普通元素,则遍历它的所有 children,递归执行 markStatic。因为所有的 elseif 和 else 节点都不在 children 中, 如果节点的 ifConditions 不为空,则遍历 ifConditions 拿到所有条件中的 block,也就是它们对应的 AST 节点,递归执行 markStatic。在这些递归过程中,一旦子节点有不是 static 的情况,则它的父节点的 static 均变成 false。
tinyant
2023/02/24
3390
深度学习Vue源码-模板编译原理
上一篇咱们主要介绍了 Vue 数据的响应式原理 对于中高级前端来说 响应式原理基本是面试 Vue 必考的源码基础类 如果不是很清楚的话基本就被 pass 了 那么今天咱们手写的模板编译原理也是 Vue 面试比较频繁的一个点 而且复杂程度是高于响应式原理的 里面主要涉及到 ast 以及大量正则匹配 大家学习完可以看着思维导图一起手写一遍加深印象哈
yyzzabc123
2022/10/03
3810
petite-vue源码剖析-v-if和v-for的工作原理
我们看到在v-if和v-for的解析过程中都会生成块对象,而且是v-if的每个分支都对应一个块对象,而v-for则是每个子元素都对应一个块对象。其实块对象不单单是管控DOM操作的单元,而且它是用于表示树结构不稳定的部分。如节点的增加和删除,将导致树结构的不稳定,把这些不稳定的部分打包成独立的块对象,并封装各自构建和删除时执行资源回收等操作,这样不仅提高代码的可读性也提高程序的运行效率。
^_^肥仔John
2022/05/09
6020
相关推荐
【Vue原理】Compile - 源码版 之 generate 节点拼接
更多 >
领券
社区富文本编辑器全新改版!诚邀体验~
全新交互,全新视觉,新增快捷键、悬浮工具栏、高亮块等功能并同时优化现有功能,全面提升创作效率和体验
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档
查看详情【社区公告】 技术创作特训营有奖征文