Vue.js源码分析(⼗七)指令篇v-if、v-el-if和v-el指令详
解
v-if 指令⽤于条件性地渲染⼀块内容。这块内容只会在指令的表达式返回true值的时候被渲染。
v-el-if,顾名思义,充当 v-if 的“el-if 块”,可以连续使⽤:
也可以使⽤ v-el 指令来表⽰ v-if 的“el 块”:
挺好理解的,就和⼤多数的语⾔的if()....el if()...el逻辑语句是⼀样的,例如:
<!DOCTYPE html>
<html lang="en">
<head>
<meta chart="UTF-8">
<title>Document</title>
<script src="cdn.jsdelivr/npm/vue@2.5.16/dist/vue.js"></script>
</head>
<body>
<script>
</script>
<div id="app">
平安夜快乐英文怎么说<p v-if="no<0">n⼩于0</p>
<p v-el-if="no==0">no等于0</p>
<p v-el>no⼤于0</p>
</div>
<script>var app = new Vue({el:'#app',data:{no:2}})</script>
</body>
</html>
渲染为:
腾飞教育
有两个注意点:
v-el和v-el-if 必须紧跟在带 v-if 或者 v-el-if 的元素之后,⼀会⼉将源码的时候会讲到为什么
因为 v-if 是⼀个指令,所以必须将它添加到⼀个元素上。但是如果想切换多个元素呢?此时可以把⼀个 <template> 元素当做不可见的包裹元素,
源码分析
爱疯4s图片
Vue内部会把v-if、v-el、v-el-if解析称为⼀个三元运算符,如果有多个v-el,则三元运算符内再嵌套⼀个三元运算符,以例⼦⾥的为例:
解析模板解析到<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-el和v-el-if⽤的)
exp: exp,
block: el
});
} el { //如果不存在v-if属性
if (getAndRemoveAttr(el, 'v-el') != null) { //如果存在el命令,则在el.el上增加⼀个el属性
el.el = true;
}
var elif = getAndRemoveAttr(el, 'v-el-if'); //如果存在v-el-if指令,则添加elif属性
if (elif) {
el.elif = elif;
}
}
}
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-el和v-el-if来说,并没有把当前对应的AST对象加到AST树中,⽽是把⾃⼰对应的AST对象添加到最近的v-if的ifConditions⾥,代码如下:
if (currentParent && !element.forbidden) { //第9223⾏如果当前对象不是根对象,且不是style和text/javascript类型script标签
if (element.elif || element.el) { //如果有elif或el指令存在(设置了v-el或v-elif指令)
processIfConditions(element, currentParent); //则调⽤processIfConditions()函数
} el if (element.slotScope) { // scoped slot //如果element是作⽤域插槽
currentParent.plain = fal;
var name = element.slotTarget || '"default"';(currentParent.scopedSlots || (currentParent.scopedSlots = {}))[name] = element;
} el {
currentParent.children.push(element);
element.parent = currentParent;
}两星期
}
processIfConditions会在之前的AST节点,也就是v-if的AST节点的ifConditions上把当前的ast对象添加进去,如下:
function processIfConditions (el, parent) { //第9421⾏解析v-el、v-el-if指令
var prev = findPrevElement(parent.children); //调⽤findPrevElement获取el之前的AST对象(只查找普通元素AST) if (prev && prev.if) { //如果prev存在,且它含有v-if指令
addIfCondition(prev, { //则调⽤addIfCondition给prev的ifConditions添加⼀条语句
exp: el.elif,
block: el
});
} el {
warn$2(
"v-" + (el.elif ? ('el-if="' + el.elif + '"') : 'el') + " " +
"ud 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] //则直接返回该元素
} el {
if ("development" !== 'production' && children[i].text !== ' ') { //开发模式下,如果该节点不是普通节点,则报错
warn$2(
"text \"" + (children[i].im()) + "\" between v-if and v-el(-if) " +
"will be ignored."
);
}
children.pop();
}
}
}
执⾏完后整个AST对象树如下:
接下来执⾏generate⽣成rendre函数时时发现有有if属性就执⾏genIf()函数:
function genIf ( //第10205⾏ //渲染v-if指令
shoot是什么意思
el,
state,
altGen,
terminatealtEmpty
) {
el.ifProcesd = true; // avoid recursion //避免递归
return genIfConditions(el.ifConditions.slice(), state, altGen, altEmpty) //调⽤genIfConditions函数
}
gatfunction 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 (p) { //拼凑三元运算符
return ("(" + (p) + ")?" + (genTernaryExp(condition.block)) + ":" + (genIfConditions(conditions, state, altGen, altEmpty))) } el {
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
ad chinagenOnce(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-el、v-el-if结构了