Vue slot principle

Slot as an important means of vUE content distribution, many students are interested in its principle, let’s explore it.

The test code


      
<html>

<head>
    <title>Vue event processing</title>
    <script src=".. /.. /.. /dist/vue.js"></script>
</head>

<body>
    <div id="demo">
        <h1>Slot processing mechanism</h1>
        <comp1>
            <span>abc</span>
        </comp1>
    </div>
    <script>
        // Declare a custom component
        Vue.component('comp1', { template: '<div><slot></slot></div>' })
     
        // Create an instance
        const app = new Vue({
            el: '#demo'
        });
        console.log(app.$options.render);        
    </script>
</body>
</html>
Copy the code

The output is as follows: < span style = “max-width: 100%; clear: both

(function anonymous() {
with(this) {return _c('div', {attrs: {"id":"demo"}},[
    _c('h1',[_v("Slot processing Mechanism")]),_v(""),
    _c('comp1',[_c('span',[_v("abc")]]),})Copy the code

Slot processing process analysis

If the v-slot command is not used, the children of the parent component will be included in the component. The next step is to see what comp1 components do when instantiated.

// core/instance/render.js
vm.$slots = resolveSlots(options._renderChildren, renderContext)
Copy the code

The resolveSlots() function retrieves render result VNodes from the parent component, which are stored in default, which is the contents of the default slot. The renderContext here is the parent component instance, which obviously needs to be retrieved if you have dynamic content.

This tells us why we can access this.$slot.default in the render function to get the default slot content

So who is using the content of $slots, which is obviously the render function of the comp1 component.

(function anonymous(
) {
with(this) {return _c('div',[_t("default"),_v(""),
	_t("foo".null, {"abc":"abc from comp"})].2)}})Copy the code

Here _t is the alias of renderSlot(), which uses the contents of $slots or $scopedSlots

//src/core/instance/render-helpers/render-slot.js
const scopedSlotFn = this.$scopedSlots[name]
let nodes
if (scopedSlotFn) {
  // ...
}
else {
  nodes = this.$slots[name] || fallback
}
Copy the code

V-slot instruction exists

When using the V-slot instruction,

<comp>
  <template v-slot:default>abc</template>
  <template v-slot:foo="ctx">{{ctx.abc}}</template>
</comp>
Copy the code

The compiled result will be in scoped slot form:

(function anonymous(
) {
with(this) {return _c('div', {attrs: {"id":"demo"}},[
  _c('h1',[_v("attr update")]),
  _c('comp', {scopedSlots:_u([
    {key:"default".fn:function(){return [_v("abc")]},proxy:true},
    {key:"foo".fn:function(ctx){return [_v(_s(ctx.abc))]}}])})],1)}})Copy the code

The _u above is the alias of resolveScopedSlots(). The v-slot argument will be the key, and the value will be the fn function argument, such as CTX above. This function is called when the root component first renders and returns the scope slot description object $scopedSlots, structured as follows:

We know that CTX comes from a child component. How does it get passed in? Take a look at comp1’s render function:

/ /...
_t("foo".null, {"abc":"abc from comp"})
Copy the code

The comp1 component’s render function calls _t (renderSlot() and passes property objects as parameter 3 from a named slot named foo in comp1. So the result is the return value of renderSlot(), which is the result of fn’s previous execution:

//src/core/instance/render-helpers/render-slot.js
const scopedSlotFn = this.$scopedSlots[name]
nodes = scopedSlotFn(props) 
Copy the code

This explains why the scope slot can use the data in the child component, because vUE converts the scope slot to function form and calls it in the child component.

conclusion

We usually use slots often can not remember the use of different slots, after principle analysis we can understand more clearly, in fact, no matter anonymous slot, named slot or scoped slot, the final compilation result is the same. In this case it could have been written the same way:

<comp1>
    <template v-slot:default>abc</template>
    <template v-slot:foo>foo</template>
</comp1>
Copy the code

Then just remember that if I want data from a parent or child, if the latter I set a property object value for v-slot:

<comp1>
    <! --foo1 from the parent component hosting comp1 -->
    <template v-slot:foo>{{foo1}}</template>
    <! - bar1 from comp1 -- -- >
    <template v-slot:bar="{bar1}">{{bar1}}</template>
</comp1>
Copy the code

What questions have not been answered, welcome to leave a message.