scope & slot parse

The scope slot in VUE has gone through several iterations, and different versions have different usage ways and limited scope (main indicator). Before starting the analysis, we might as well explore the use of scope slot in each period based on the axis of time.

Before you start, it’s important to be clear that a non-scoped slot simply specifies the name of the slot, so it was unchanged until V2.6.

Tip: By rule, tags with no slot=”name” attribute specify slot=”default” by default.

The evolution of scope usage

The scope era

The scope slot instruction of this era is scope. Can only be used on template elements.

<template slot="head" scope="row">
  <h1>scope - {{row.name}}</h1>
</template>
Copy the code

Slot – scope

This era was characterized by freedom, and named slot instructions could be spread across all known components.

<self-component slot-scope="row" slot="header"></self-component>

<div slot-scope="row" slot="header"></div>

<template slot-scope="row" slot="header"></template>
Copy the code

V – slot time

This era is characterized by the simplicity of combining the designation of scope and slot name in a single instruction v-slot.

<div v-slot:head="row"></div>

<! -- non-scoped slot -->
<div v-slot:head></div>
Copy the code

Unload the last non-scoped slot

<div slot="header">header</div>
Copy the code

Analysis of the

Step 1: The nature of the slot

Before understanding how to parse, we need to briefly understand what the nature of the slot is, so that it is convenient for further parsing, and the goal of parsing.

Just to be clear

  • For a non-scoped slot, you need to know the name of the slot and the name of the slotrenderFunction. This is the ultimate parsing target for non-scoped slots.
  • For a scoped slot, you need to know not only the name and render function, but also the name of the variable that identifies the scope.

The above analysis shows that for non-scoped slots:

<div slot="header">header</div>
Copy the code

The required code may be as follows:

const slot = {
  header: () = > {
    return // VNode corresponding to the slot}}Copy the code

Naturally, for scoped slots:

<template v-slot:header="scope">
  <div>{{scope.header}}</div>
</template>
Copy the code

The expected final resolution is

const slotScope = {
  header: function(scope){
    return // VNode corresponding to the slot}}Copy the code

As can be seen from the above analysis, what needs to be resolved here is:

  • The name of the slotheader
  • Slot scope objectscope(Scope slots are required)

But because there are historical versions, you should wrap all cases in when parsing

parse


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]
            }
        }
    }
}

// The environment variable identifies whether v-slot can be used
process.env.NEW_SLOT_SYNTAX = true

const slotRE = /^v-slot(:|$)|^#/

function parse(el) {
  let slotScope
  // Parse the scope variable name
  if(el.tag === 'template'){
    el.slotScope = getAndRemoveAttr(el, 'scope') || getAndRemoveAttr(el, 'slot-scope')}else if(slotScope = getAndRemoveAttr(el, 'slot-scope')){
    el.slotScope = slotScope
  }
  // Parse the slot name
  const slotTarget = getAndRemoveAttr(el, 'slot')
  if(slotTarget){
    el.slotTarget = slotTarget === '" "' ? '"default"' : slotTarget
  }
  /* 2.6 and later */
  if(process.env.NEW_SLOT_SYNTAX){
    if(el.tag === 'template') {// v-slot on <template>
      const slotBinding = getAndRemoveAttrByRegex(el, slotRE)
      if (slotBinding) {
        const { name, dynamic } = getSlotName(slotBinding)
        el.slotTarget = name
        el.slotTargetDynamic = dynamic
        el.slotScope = slotBinding.value || emptySlotScopeToken // force it into a scoped slot for perf}}else {
      // Even if the v-slot is bound to the component, the content is put back into a template
      const slotBinding = getAndRemoveAttrByRegex(el, slotRE)
      if (slotBinding) {
        const slots = el.scopedSlots || (el.scopedSlots = {})
        const { name, dynamic } = getSlotName(slotBinding)
        / / will < compoentName v - slot = "XXXX" > DSDD < / a >
        
      
       
      
        const slotContainer = slots[name] = createASTElement('template'[], el) slotContainer.slotTarget = name slotContainer.slotTargetDynamic = dynamic slotContainer.children = el.children.filter((c: any) = > {
          if(! c.slotScope) {// The scope slot cannot contain any other scope slot, otherwise it will be ignored
            c.parent = slotContainer
            return true
          }
        })
        slotContainer.slotScope = slotBinding.value || emptySlotScopeToken
        el.children = []
        el.plain = false}}}}Copy the code

Code parsing

The above code is compatible with the resolution of both scoped and non-scoped slots of all vUE ages. In fact, you can see that for v-slot defined slots, whether the template content is wrapped in the template tag or not, the slot content is eventually included in the Template tag.

conclusion

The analysis shows that the way to use scoped and non-scoped slots in VUE is on the template tag.

For Scope

  • Only in thetemplateUse on labels
  • useslot=nameSpecify a name

For slot-scope

  • Can be used on any legal tag
  • useslot=nameSpecify a name

For V-slot

  • Only in thetemplatecomponentFor use on
    • When used on a component, it is still moved to the createdtemplateThe label to go up and
  • usev-slot:name="scope"To specify the name and scope variables

Because the syntax changes frequently, use the same syntax to facilitate maintenance. There are also two versions of the use of dynamic slot names

  • :slot="name" , nameIs a dynamic name
  • v-slot[dynamicSlotName]"Or use abbreviations#[dynamicSlotName]