# 新旧Slot语法差异

Vue2.6.X版本中引入了v-slot语法,分析一下slot新旧语法间的差异。v-slot

# 编译阶段

# parserHtml 阶段

在html解析阶段的closeElement方法中会调用processElement方法来处理节点的ast,其中会调用processSlotContent方法来处理ast中的slot相关属性

function processSlotContent (el) {

    let slotScope
    if(el.tag === 'template'){
        slotScope = getAndRemoveAttr(el, 'scope') // 兼容旧语法
        el.slotScope = slotScope || getAndRemoveAttr(el, 'slot-scope')
    }else if((slotScope = getAndRemoveAttr(el, 'slot-scope'))){
        el.slotScope = slotScope
    }

    const slotTarget = getBindingAttr(el, 'slot')
    if (slotTarget) {
        el.slotTarget = slotTarget === '""' ? '"default"' : slotTarget
        el.slotTargetDynamic = !!(el.attrsMap[':slot'] || el.attrsMap['v-bind:slot'])
        if (el.tag !== 'template' && !el.slotScope) {
      addAttr(el, 'slot', slotTarget, getRawBindingAttr(el, 'slot')) // 作用未知
    }
    }

    if (process.env.NEW_SLOT_SYNTAX){
        if (el.tag === 'template') {
            const slotBinding = getAndRemoveAttrByRegex(el, slotRE)
            if (slotBinding) {
                const { name, dynamic } = getSlotName(slotBinding)
                el.slotTarget = name
                el.slotTargetDynamic = dynamic
                el.slotScope = slotBinding.value || emptySlotScopeToken // emptySlotScopeToken '_empty_'
            }
        }else{
            // v-slot 只有一种情况不作用在 template 上
            // 即当前slot为default 且当前元素为自定义组件
            const slotBinding = getAndRemoveAttrByRegex(el, slotRE)
            if (slotBinding) {
                const slots = el.scopedSlots || (el.scopedSlots = {})
                const { name, dynamic } = getSlotName(slotBinding)
                const slotContainer = slots[name] = createASTElement('template', [], el)
                slotContainer.slotTarget = name
                slotContainer.slotTargetDynamic = dynamic
                slotContainer.children = el.children.filter((c: any) => {
                    if (!c.slotScope) {
                        c.parent = slotContainer
                        return true
                    }
                })
                slotContainer.slotScope = slotBinding.value || emptySlotScopeToken
                el.children = []
                el.plain = false
            }
        }
    }
}

仔细观察上面的代码可以得知,这段代码在处理旧的slot slot-scope语法时,只是分析属性绑定,给当前节点的ast添加了3个属性分别是

  • slotTarget slot的name
  • slotTargetDynamic 是否是动态的slot
  • slotScope slotScope 的变量名称

在处理v-slot语法时,作用在template上的情况与上面一致。差异点在于处理v-slot作用在自定义组件上时,会在当前的节点ast上添加slotScopes对象,同时将提取出来的slot配置生成一个template的ast节点添加到这个对象中。同时将该节点下所有没有slotScop的节点添加到child属性中去,再去清空当前节点本身的子节点。

接着回到processElement的原始调用方法CloseElement方法中去

function CloseElement(element){
    if (currentParent && !element.forbidden) {
        if (element.elseif || element.else) {
            processIfConditions(element, currentParent)
        }else{
            if (element.slotScope) {
              const name = element.slotTarget || '"default"'
              ;(currentParent.scopedSlots || (currentParent.scopedSlots = {}))[name] = element
            }
            currentParent.children.push(element)
            element.parent = currentParent
        }
    }
    element.children = element.children.filter(c => !(c: any).slotScope)
}