需要明确的是 v-if 并不是一个指令,而是一组指令的合集。一般来说会包含
v-ifv-else-ifv-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-ifv-else-ifv-else
在使用时需要遵循以下细节:
v-else-if的上一个兄弟元素必须包含v-else-if或v-if指令v-else的上一个兄弟元素必须包含v-else-if或v-ifv-else后的v-else-if将会被舍弃(也就是说,v-else将会是这一串指令的终结)v-else、v-else-if、v-if指令之间的文本节点将会被舍弃- 和
if语句一样,一次只能走一个分支语句,一系列v-if指令也只会渲染一个元素