Vue.js 源码分析(十七) 指令篇 v-ifv-else-if和v-else 指令详解
Posted greatdesert
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Vue.js 源码分析(十七) 指令篇 v-ifv-else-if和v-else 指令详解相关的知识,希望对你有一定的参考价值。
v-if 指令用于条件性地渲染一块内容。这块内容只会在指令的表达式返回 truthy 值的时候被渲染。
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/[email protected]/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>
渲染为:
有两个注意点:
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结构了
以上是关于Vue.js 源码分析(十七) 指令篇 v-ifv-else-if和v-else 指令详解的主要内容,如果未能解决你的问题,请参考以下文章
Vue.js 源码分析(二十) 指令篇 v-once指令详解
Vue 2.0 深入源码分析 基础篇 methods属性详解
v83.01 鸿蒙内核源码分析(编码方式篇) | 机器指令是如何编码的 | 百篇博客分析OpenHarmony源码