1. Start with simple anonymous slots

Vue.component('button-counter', {
  template: '
      
I'm default
'
}) Copy the code
new Vue({
	el: '#app',
	template: '
      
        I am slot incoming content 
      '
})
Copy the code

Here we register a button-counter component and use it. The result rendered above is I am slot content

Principle analysis:

< div style = “box-sizing: border-box; color: RGB (74, 74, 74); display: block; line-height: 21px; white-space: inherit! Important;”

(function anonymous(
) {
with(this){return _c('div',[_t("default",[_v("I'm the default.")])],2)}
})
Copy the code

_v is used to create a normal text node, mainly the _t function, where _t is shorthand for renderSlot

functioninstallRenderHelpers (target) { ... target._t = renderSlot; . }Copy the code
function renderSlot (
  name,
  fallback,
  props,
  bindObject
) {
  var scopedSlotFn = this.$scopedSlots[name];
  var nodes;
  nodes = scopedSlotFn(props) || fallback;
  return nodes;
}
Copy the code

I’m just going to simplify the renderSlot function, and I’m going to pass in the name and the fallback, and I’m going to say a little bit about the name property, which we defined above

Vue.component('button-counter', {
  template: '
      
I'm default
'
}) Copy the code

Here slot does not give props a name, so the default is default. We can give it a name, for example

Vue.component('button-counter', {
  template: '
      
I'm default
'
}) Copy the code

So when you use it

new Vue({
  el: '#app',
  template: '
      
        
      '
})
Copy the code

The above use of slot has been deprecated in version 2.6.0 and replaced with a new v-slot, but you can still write it this way. The source code still contains the compatibility treatment for slot. Let’s take a look at how to write v-slot and what to pay attention to

new Vue({
  el: '#app',
  template: '
      
       
      '
})
Copy the code

Note that v-slot needs to be used on the template tag, not the span tag as above

Back to the renderSlot function, name is the default value, and the second argument fallback is the default value for the slot node in the component

var scopedSlotFn = this.$scopedSlots[name];
var nodes;
nodes = scopedSlotFn(props) || fallback;
return nodes;
Copy the code

If this.$scopredSlots has a value for this.$scopredSlots, call the return function to generate a return value of nodes. Otherwise, fallback is returned. There is this.$scopredSlots variable involved, so let’s look at the value. Let’s look at vm

function initRender (vm) {
  ...
  vm.$slots= resolveSlots(options._renderChildren, renderContext); . }Copy the code

The resolveSlots function classifies and filters the Children node and returns slots

function resolveSlots (
    children,
    context
  ) {
    if(! children || ! children.length) {return {}
    }
    var slots = {};
    for (var i = 0, l = children.length; i < l; i++) {
      var child = children[i];
      var data = child.data;
      // remove slot attribute if the node is resolved as a Vue slot node
      if (data && data.attrs && data.attrs.slot) {
        delete data.attrs.slot;
      }
      // named slots should only be respected if the vnode was rendered in the
      // same context.
      if((child.context === context || child.fnContext === context) && data && data.slot ! = null) {// If slot exists (slot="header"Var name = data.slot; var slot = (slots[name] || (slots[name] = [])); // If it's a tempalte element, add the children of the template to the array. That's why your template tag doesn't render as another tag to the pageif (child.tag === 'template') {
          slot.push.apply(slot, child.children || []);
        } else{ slot.push(child); }}else{/ / if there is no default is default (slots. The default | | (slots. Default = [])), push (child); } } // ignore slots that contains only whitespacefor (var nameThe $1 in slots) {
      if (slots[nameThe $1].every(isWhitespace)) {
        delete slots[nameThe $1]; }}return slots
  }
Copy the code

The _renderChildren function classifies VNodes with the same name as children into an array, and finally returns an object with the corresponding name as key and array of nodes containing the corresponding name

The next _render function gets vm.$scopedSlots from normalizeScopedSlots

vm.$scopedSlots = normalizeScopedSlots(
  _parentVnode.data.scopedSlots,
  vm.$slots,
  vm.$scopedSlots
);
Copy the code

Scope slot

Sometimes we need to get child components of some internal data, such as access to the value of the MSG below, but the current scope of execution is the parent component instance, through this. MSG is to get less than the value of the child components inside we need in < slot > element dynamic binding a MSG object attribute, then the parent component could be obtained by the following way

Vue.component('button-counter', {
  data () {
    return {
        msg: 'hi'
    }
 },
  template: '
      
I am the default content
'
}) Copy the code
new Vue({
  el: '#app',
  template: '<button-counter><template scope="msg"><span>{{props.msg}}</span></template></button-counter>'
})
Copy the code

This was written before 2.5, after which slot-scope was used

new Vue({
  el: '#app',
  template: '<button-counter><template slot-scope="msg"><span>{{props.msg}}</span></template></button-counter>'
})
Copy the code

2.6 After that, use v-slot instead of the preceding two notations

new Vue({
  el: '#app',
  template: '<button-counter><template v-slot="msg"><span>{{props.msg}}</span></template></button-counter>'
})
Copy the code

The MSG is available because renderSlot is passed as props. As you can see, the third argument to the render function _t is props

(function anonymous(
) {
with(this){return _c('div',[_t("default",[_v("I'm the default.")] and {"msg":msg})],2)}
})
Copy the code
var scopedSlotFn = this.$scopedSlots[name];
var nodes;
nodes = scopedSlotFn(props) || fallback;
return nodes;
Copy the code

The render function can see the props passed in and get the value of MSG

(function anonymous(
) {
with(this){return _c('button-counter',{scopedSlots:_u([{key:"default",fn:function(props){return [_c('span',[_v(_s(props.msg))])]}}])})}
})
Copy the code

The last

This is just a brief description of a few key points, slot scope and support for deconstructive assignment, support for dynamic slot names and other writing methods, there are a lot of details need to be more in-depth to understand the source code.

If you think it will help you, please click the Star ha Github address