需要明确的是 v-if
并不是一个指令,而是一组指令的合集。一般来说会包含
v-if
v-else-if
v-else
这三种,其中 v-if
需要在开头,v-else
需要在结尾,中间可以有若干个 v-else-if
,这样子就构成了一系列的条件显示语句。按照vue的要求
v-else-if
必须紧跟在v-if
元素之后v-else
必须紧跟在v-else-if
或v-if
元素之后
介于上述规则,对于 v-if
相关的指令,需要进行两步解析
- 第一步是在开始标签解析结束时,需要将
v-if
相关的指令挂载到 ast 元素上,方便后面处理 - 第二步是在整个元素标签结束时,需要验证
v-else-if
和v-else
使用的是否合法,然后在进行对应处理 - 这里还需要注意,对于一组
v-else
、v-else-if
、v-else
元素来说,最终只会生成一个元素,而不是多个。
根据上面的分析,下面开始展开解释
解析第一步,ast 指令标记
这一步发生的时机在 开始标签解析结束时,这时的属性解析已经完毕,所以可以很轻松的对 v-if
相关的指令做出标记。
开始之前,先了解一个工具函数,主要是用来读取 attrList
中的同名属性,然后将其从列表中删除
function getAndRemoveAttr(el, name) {
if(el.attrMap[name] != null) {
const attrList = el.attrList
for(let i = 0; i < attrList.length; i++){
if(attrList[i].name === name) {
attrList.splice(i, 1)
return el.attrMap[name]
}
}
}
}
这里要把指令删除是因为指令不是 html
的原生属性,不需要最终显示在标签上。(有 v-pre 属性的元素除外)
v-if
指令解析
function processIf(el) {
const exp = getAndRemoveAttr(el, 'v-if')
if(exp) {
el.if = exp;
el.ifConditions = [{exp, block: el}]
} else {
const elseIfExp = getAndRemoveAttr(el, 'v-else-if')
if(elseIfExp){
el.elseif = elseIfExp
} else {
el.else = true
}
}
}
第二步,在标签结束阶段验证 v-else-if 和 v-else 的合法性
这一步中,需要检查 v-eles-if
和 v-else
的合法性,这里有一些细节需要注意:
v-if
一系列的元素的指令应该是同一层及的元素v-else-if
上一个元素必须包含v-eles-if
或v-if
指令- 对于
v-if
系列中存在的文本,直接舍弃。
按照以上的规则,可以得出以下代码:
function findPrveIfElemnt(children) {
const i = children.length
while(i--){
if(children[i].type === 1){
return children[i]
} else {
// 非 元素类的节点 移除
children.pop()
}
}
}
function parseIf(el) {
const prev = findPrveIfElemnt(el.children)
if(prev && prev.if){
prev.ifConditions.push({
exp: el.elseif,
block: el
})
} else {
// 警告信息,告诉用户使用错误
// 需要注意的是,如果紧邻的元素不是 v-if 指令元素,则这个元素会被单独渲染,而且不受 v-else-if 的限制
}
}
总结
vue
中的条件渲染包含了
v-if
v-else-if
v-else
在使用时需要遵循以下细节:
v-else-if
的上一个兄弟元素必须包含v-else-if
或v-if
指令v-else
的上一个兄弟元素必须包含v-else-if
或v-if
v-else
后的v-else-if
将会被舍弃(也就是说,v-else
将会是这一串指令的终结)v-else
、v-else-if
、v-if
指令之间的文本节点将会被舍弃- 和
if
语句一样,一次只能走一个分支语句,一系列v-if
指令也只会渲染一个元素