v-if 指令用于条件性地渲染一块内容。这块内容只会在指令的表达式返回true值的时候被渲染。

v-else-if,顾名思义,充当 v-if 的“else-if 块”,可以连续使用:

也可以使用 v-else 指令来表示 v-if 的“else 块”:

挺好理解的,就和大多数的语言的if()....else if()...else逻辑语句是一样的,例如:

  1. <!DOCTYPE html>
  2. <html lang="en">
  3. <head>
  4. <meta charset="UTF-8">
  5. <title>Document</title>
  6. <script src="https://cdn.jsdelivr.net/npm/vue@2.5.16/dist/vue.js"></script>
  7. </head>
  8. <body>
  9. <script>
  10. Vue.config.productionTip=false;
  11. Vue.config.devtools=false;
  12. </script>
  13. <div id="app">
  14. <p v-if="no<0">n小于0</p>
  15. <p v-else-if="no==0">no等于0</p>
  16. <p v-else>no大于0</p>
  17. </div>
  18. <script>var app = new Vue({el:'#app',data:{no:2}})</script>
  19. </body>
  20. </html>

writer by:大沙漠 QQ:22969969

渲染为:

有两个注意点:

  v-else和v-else-if 必须紧跟在带 v-if 或者 v-else-if 的元素之后,一会儿将源码的时候会讲到为什么

  因为 v-if 是一个指令,所以必须将它添加到一个元素上。但是如果想切换多个元素呢?此时可以把一个 <template> 元素当做不可见的包裹元素,

源码分析


Vue内部会把v-if、v-else、v-else-if解析称为一个三元运算符,如果有多个v-else,则三元运算符内再嵌套一个三元运算符,以例子里的为例:

解析模板解析到<p v-if="no<0">n小于0</p>这个DOM元素时会执行到processIf()函数

  1. function processIf (el) { //第9402行 解析v-if指令
  2. var exp = getAndRemoveAttr(el, 'v-if'); //获取表达式,例如:"no<0"
  3. if (exp) { //如果存在v-if属性
  4. el.if = exp; //增加if属性
  5. addIfCondition(el, { //调用addIfCondition()函数给el增加一个ifConditions属性,值是一个对象,其中 exp表示当前v-if的值,block是当前AST对象的引用(一会儿给v-else和v-else-if用的)
  6. exp: exp,
  7. block: el
  8. });
  9. } else { //如果不存在v-if属性
  10. if (getAndRemoveAttr(el, 'v-else') != null) { //如果存在else命令,则在el.else上增加一个else属性
  11. el.else = true;
  12. }
  13. var elseif = getAndRemoveAttr(el, 'v-else-if'); //如果存在v-else-if指令,则添加elseif属性
  14. if (elseif) {
  15. el.elseif = elseif;
  16. }
  17. }
  18. }
  19. function addIfCondition (el, condition) { //第9453行 增加一个ifConditions属性,
  20. if (!el.ifConditions) {
  21. el.ifConditions = []; //如果ifConditions属性不存在则初始化为一个空数组
  22. }
  23. el.ifConditions.push(condition); //将参数condition这个对象push进来
  24. }

对于v-if节点只是增加一个if和ifConditions属性,对于<p v-if="no<0">n小于0</p>来说,对应的AST对象增加的属性如下:

解析模板时,对于v-else和v-else-if来说,并没有把当前对应的AST对象加到AST树中,而是把自己对应的AST对象添加到最近的v-if的ifConditions里,代码如下:

  1. if (currentParent && !element.forbidden) { //第9223行 如果当前对象不是根对象, 且不是style和text/javascript类型script标签
  2. if (element.elseif || element.else) { //如果有elseif或else指令存在(设置了v-else或v-elseif指令)
  3. processIfConditions(element, currentParent); //则调用processIfConditions()函数
  4. } else if (element.slotScope) { // scoped slot //如果element是作用域插槽
  5. currentParent.plain = false;
  6. var name = element.slotTarget || '"default"';(currentParent.scopedSlots || (currentParent.scopedSlots = {}))[name] = element;
  7. } else {
  8. currentParent.children.push(element);
  9. element.parent = currentParent;
  10. }
  11. }

processIfConditions会在之前的AST节点,也就是v-if的AST节点的ifConditions上把当前的ast对象添加进去,如下:

  1. function processIfConditions (el, parent) { //第9421行 解析v-else、v-else-if指令
  2. var prev = findPrevElement(parent.children); //调用findPrevElement获取el之前的AST对象(只查找普通元素AST)
  3. if (prev && prev.if) { //如果prev存在,且它含有v-if指令
  4. addIfCondition(prev, { //则调用addIfCondition给prev的ifConditions添加一条语句
  5. exp: el.elseif,
  6. block: el
  7. });
  8. } else {
  9. warn$2(
  10. "v-" + (el.elseif ? ('else-if="' + el.elseif + '"') : 'else') + " " +
  11. "used on element <" + (el.tag) + "> without corresponding v-if."
  12. );
  13. }
  14. }
  15.  
  16. function findPrevElement (children) { //第9436行 查找children前一个文本AST对象
  17. var i = children.length;
  18. while (i--) { //遍历children,从后开始
  19. if (children[i].type === 1) { //如果是普通节点
  20. return children[i] //则直接返回该元素
  21. } else {
  22. if ("development" !== 'production' && children[i].text !== ' ') { //开发模式下,如果该节点不是普通节点,则报错
  23. warn$2(
  24. "text \"" + (children[i].text.trim()) + "\" between v-if and v-else(-if) " +
  25. "will be ignored."
  26. );
  27. }
  28. children.pop();
  29. }
  30. }
  31. }

执行完后整个AST对象树如下:

接下来执行generate生成rendre函数时时发现有有if属性就执行genIf()函数:

  1. function genIf ( //第10205行 //渲染v-if指令
  2. el,
  3. state,
  4. altGen,
  5. altEmpty
  6. ) {
  7. el.ifProcessed = true; // avoid recursion //避免递归
  8. return genIfConditions(el.ifConditions.slice(), state, altGen, altEmpty) //调用genIfConditions函数
  9. }
  10.  
  11. function genIfConditions ( //第10215行 拼凑if表达式 conditions:比如:[{exp: "ok", block: {…}}]
  12. conditions,
  13. state,
  14. altGen,
  15. altEmpty
  16. ) {
  17. if (!conditions.length) { //如果conditions不存在
  18. return altEmpty || '_e()' //则直接返回altEmpty
  19. }
  20.  
  21. var condition = conditions.shift(); //获取内容,比如:{exp: "no<0", block: {…}}
  22. if (condition.exp) { //拼凑三元运算符
  23. return ("(" + (condition.exp) + ")?" + (genTernaryExp(condition.block)) + ":" + (genIfConditions(conditions, state, altGen, altEmpty)))
  24. } else {
  25. return ("" + (genTernaryExp(condition.block)))
  26. }
  27.  
  28. // v-if with v-once should generate code like (a)?_m(0):_m(1)
  29. function genTernaryExp (el) { //再次调用genElement()函数
  30. return altGen
  31. ? altGen(el, state)
  32. : el.once
  33. ? genOnce(el, state)
  34. : genElement(el, state)
  35. }
  36. }

最后渲染的render函数为:

  1. _c('div',{attrs:{"id":"app"}},[(no<0)?_c('p',[_v("n小于0")]):(no==0)?_c('p',[_v("no等于0")]):_c('p',[_v("no大于0")])])

其中

  1. (no<0)?_c('p',[_v("n小于0")]):(no==0)?_c('p',[_v("no等于0")]):_c('p',[_v("no大于0")])

就是对应的例子里的v-if、v-else、v-else-if结构了

Vue.js 源码分析(十七) 指令篇 v-if、v-else-if和v-else 指令详解的更多相关文章

  1. Vue.js 源码分析(十一) 基础篇 过滤器 filters属性详解

    Vue.js 允许你自定义过滤器,可被用于一些常见的文本格式化.过滤器可以用在两个地方:双花括号插值和 v-bind 表达式 (后者从 2.1.0+ 开始支持).过滤器应该被添加在 JavaScrip ...

  2. Vue.js 源码分析(十) 基础篇 ref属性详解

    ref 被用来给元素或子组件注册引用信息.引用信息将会注册在父组件的 $refs 对象上.如果在普通的 DOM 元素上使用,引用指向的就是 DOM 元素:如果用在子组件上,引用就指向组件实例,例如: ...

  3. Vue.js 源码分析(四) 基础篇 响应式原理 data属性

    官网对data属性的介绍如下: 意思就是:data保存着Vue实例里用到的数据,Vue会修改data里的每个属性的访问控制器属性,当访问每个属性时会访问对应的get方法,修改属性时会执行对应的set方 ...

  4. Vue.js 源码分析(三) 基础篇 模板渲染 el、emplate、render属性详解

    Vue有三个属性和模板有关,官网上是这样解释的: el ;提供一个在页面上已存在的 DOM 元素作为 Vue 实例的挂载目标 template ;一个字符串模板作为 Vue 实例的标识使用.模板将会 ...

  5. Vue.js 源码分析(二) 基础篇 全局配置

    Vue.config是一个对象,包含Vue的全局配置,可以在启动应用之前修改下列属性,如下: ptionMergeStrategies        ;自定义合并策略的选项silent         ...

  6. Vue.js 源码分析(九) 基础篇 生命周期详解

    先来看看官网的介绍: 主要有八个生命周期,分别是: beforeCreate.created.beforeMount.mounted.beforeupdate.updated   .beforeDes ...

  7. Vue.js 源码分析(八) 基础篇 依赖注入 provide/inject组合详解

    先来看看官网的介绍: 简单的说,当组件的引入层次过多,我们的子孙组件想要获取祖先组件的资源,那么怎么办呢,总不能一直取父级往上吧,而且这样代码结构容易混乱.这个就是这对选项要干的事情 provide和 ...

  8. Vue.js 源码分析(七) 基础篇 侦听器 watch属性详解

    先来看看官网的介绍: 官网介绍的很好理解了,也就是监听一个数据的变化,当该数据变化时执行我们的watch方法,watch选项是一个对象,键为需要观察的数据名,值为一个表达式(函数),还可以是一个对象, ...

  9. Vue.js 源码分析(十三) 基础篇 组件 props属性详解

    父组件通过props属性向子组件传递数据,定义组件的时候可以定义一个props属性,值可以是一个字符串数组或一个对象. 例如: <!DOCTYPE html> <html lang= ...

随机推荐

  1. 解决java导入project出现红叉

    1.右击,import,选择需要导入的文件组. D:\softwar\seeyon\A8\ApacheJetspeed\webapps\seeyon\WEB-INF\lib  全选,打开,finish ...

  2. 30段极简Python代码:这些小技巧你都Get了么

    学 Python 怎样才最快,当然是实战各种小项目,只有自己去想与写,才记得住规则.本文是 30 个极简任务,初学者可以尝试着自己实现:本文同样也是 30 段代码,Python 开发者也可以看看是不是 ...

  3. .NET MVC5简介(一)

    就像是.NET Framework WebApi与.NET Core WebApi一样,.NET Framework MVC与.NET Core MVC的区别,也是框架的之间的区别.本系列先首先从.N ...

  4. JS基础语法---函数---介绍、定义、函数参数、返回值

    函数: 把一坨重复的代码封装,在需要的时候直接调用即可 函数的作用: 代码的重用 函数需要先定义,然后才能使用 函数名字:要遵循驼峰命名法 函数一旦重名,后面的会把前面的函数覆盖 Ctrl +鼠标左键 ...

  5. js清除定时器注意点

    如何这篇文章所述:https://www.cnblogs.com/mmykdbc/p/7418575.html js多次调用创建定时器的函数,会使定时器速度越来越快,多次调用定时器的使用场景比如:监听 ...

  6. 从0系统学Android-1.4日志工具的使用

    更多精品文章分类 1.4 日志工具 简单介绍一下日志工具,对以后的开发非常有用 1.4.1 使用日志工具 Log Log 日志工具类提供了 5 个方法来供我们打印信息(级别逐渐提高) Log.v(): ...

  7. Linux—运行yum报错:No module named yum

    产生原因:yum基于python写的,根据报错信息提示,是yum的python版本对应不上目前python环境的版本导致的.也就是说 有人升级或者卸载了python. 解决方式: # 查看yum版本 ...

  8. Linux 登录 MySQL 报错, 解决bash: mysql: command not found 的方法

    原因:这是由于系统默认会查找/usr/bin下的命令,如果这个命令不在这个目录下,当然会找不到命令,我们需要做的就是映射一个链接到/usr/bin目录下,相当于建立一个链接文件. 首先得知道mysql ...

  9. iPad替代midi键盘

    下载安装rtpMIDI (网络MIDI驱动程序) 打开rtpMIDI,在“My session”那里按下+,就会自动显示你的电脑的名字 检查Bonjour服务正常运行,iPad与pc网络正常连接 iP ...

  10. HDU 4729 An Easy Problem for Elfness(树链剖分边权+二分)

    题意 链接:https://cn.vjudge.net/problem/HDU-4729 给你n个点,然你求两个点s和t之间的最大流.而且你有一定的钱k,可以进行两种操作 1.在任意连个点之间建立一个 ...