v-if 指令用於條件性地渲染一塊內容。這塊內容只會在指令的表達式返回 truthy 值的時候被渲染。 v-else-if,顧名思義,充當 v-if 的“else-if 塊”,可以連續使用: 也可以使用 v-else 指令來表示 v-if 的“else 塊”: 挺好理解的,就和大多數的語言的if() ...
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/[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結構了