v-if parse

552 阅读3分钟

需要明确的是 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-ifv-if 元素之后

介于上述规则,对于 v-if 相关的指令,需要进行两步解析

  • 第一步是在开始标签解析结束时,需要将 v-if 相关的指令挂载到 ast 元素上,方便后面处理
  • 第二步是在整个元素标签结束时,需要验证 v-else-ifv-else 使用的是否合法,然后在进行对应处理
  • 这里还需要注意,对于一组 v-elsev-else-ifv-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-ifv-else 的合法性,这里有一些细节需要注意:

  • v-if 一系列的元素的指令应该是同一层及的元素
  • v-else-if 上一个元素必须包含 v-eles-ifv-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

在使用时需要遵循以下细节:

  1. v-else-if 的上一个兄弟元素必须包含 v-else-ifv-if 指令
  2. v-else 的上一个兄弟元素必须包含 v-else-ifv-if
  3. v-else 后的 v-else-if 将会被舍弃(也就是说,v-else 将会是这一串指令的终结)
  4. v-elsev-else-ifv-if 指令之间的文本节点将会被舍弃
  5. if 语句一样,一次只能走一个分支语句,一系列 v-if 指令也只会渲染一个元素