zoukankan      html  css  js  c++  java
  • Vue.js 源码分析(十七) 指令篇 v-if、v-else-if和v-else 指令详解

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

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

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

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

    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>Document</title>
        <script src="https://cdn.jsdelivr.net/npm/vue@2.5.16/dist/vue.js"></script>
    </head>
    <body>
        <script>
            Vue.config.productionTip=false;
            Vue.config.devtools=false;
        </script>
        <div id="app">
            <p v-if="no<0">n小于0</p>
            <p v-else-if="no==0">no等于0</p>
            <p v-else>no大于0</p>
        </div>
        <script>var app = new Vue({el:'#app',data:{no:2}})</script>
    </body>
    </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()函数

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

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

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

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

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

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

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

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

    function genIf (                //第10205行  //渲染v-if指令
      el,
      state,
      altGen,
      altEmpty
    ) {
      el.ifProcessed = true; // avoid recursion                                   //避免递归
      return genIfConditions(el.ifConditions.slice(), state, altGen, altEmpty)    //调用genIfConditions函数
    }
      
    function genIfConditions (      //第10215行 拼凑if表达式 conditions:比如:[{exp: "ok", block: {…}}] 
      conditions,
      state,
      altGen,
      altEmpty
    ) {
      if (!conditions.length) {             //如果conditions不存在
        return altEmpty || '_e()'             //则直接返回altEmpty
      }
    
      var condition = conditions.shift();    //获取内容,比如:{exp: "no<0", block: {…}}
      if (condition.exp) {                   //拼凑三元运算符
        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) {         //再次调用genElement()函数
        return altGen
          ? altGen(el, state)
          : el.once
            ? genOnce(el, state)
            : genElement(el, state)
      }
    }

    最后渲染的render函数为:

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

    其中

    (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结构了

  • 相关阅读:
    太赞了!两个技巧帮你记住复杂 Linux 命令!
    Spring Cloud 学习 之 Spring Cloud Eureka(源码分析)
    Spring Cloud 学习 之 Spring Cloud Eureka(架构)
    Spring Cloud 学习 之 Spring Cloud Eureka(搭建)
    Spring Cloud 学习 之 Spring Cloud Eureka(概述)
    Spring Boot学习 之 Spring Boot Actuator(一)
    java基础篇 之 final关键字
    spring学习笔记(九)事务学习(上)
    JAVA基础篇 之 类的初始化
    JAVA基础篇 之 finalize()方法的作用
  • 原文地址:https://www.cnblogs.com/greatdesert/p/11127935.html
Copyright © 2011-2022 走看看