src/core/instance/render.js

export function initRender (vm: Component) {
    vm._vnode = null // the root of the child tree
    const options = vm.$options

    const parentVnode = vm.$vnode = options._parentVnode // the placeholder node in parent tree
    const renderContext = parentVnode && parentVnode.context

    // Process component slot, return slot slot object
    vm.$slots = resolveSlots(options._renderChildren, renderContext)
    vm.$scopedSlots = emptyObject

   
    vm._c = (a, b, c, d) = > createElement(vm, a, b, c, d, false)
    vm.$createElement = (a, b, c, d) = > createElement(vm, a, b, c, d, true)

    const parentData = parentVnode && parentVnode.data

    if(process.env.NODE_ENV ! = ='production') {
        defineReactive(vm, '$attrs', parentData && parentData.attrs || emptyObject, () => { ! isUpdatingChildComponent && warn(`$attrs is readonly.`, vm)
        }, true)
        defineReactive(vm, '$listeners', options._parentListeners || emptyObject, () => { ! isUpdatingChildComponent && warn(`$listeners is readonly.`, vm)
        }, true)}else {
        defineReactive(vm, '$attrs', parentData && parentData.attrs || emptyObject, null.true)
        defineReactive(vm, '$listeners', options._parentListeners || emptyObject, null.true)}}Copy the code

InitRender converts children VNode to a slots object:

vm.$slots = resolveSlots(options._renderChildren, renderContext)
Copy the code

src/core/instance/render-helpers/resolve-slots.js

export function resolveSlots (children: ? Array<VNode>,//The contents of the placeholder Vnode context:? Component//The Vue instance where the placeholder Vnode resides) :{ [key: string]: Array<VNode> } {
    const slots = {}

    // If the current component is referenced without child nodes, an empty object is returned
    if(! children) {return slots
    }

    const defaultSlot = []

    // Iterate over each child node
    for (let i = 0, l = children.length; i < l; i++) {
        const child = children[i]
        const data = child.data     // The data property of the current child node

        // remove slot attribute if the node is resolved as a Vue slot node

        /* Remove slot attributes *  * VNode data = {attrs:{slot: "ABC "}, slot:" ABC "} * Remove slot */ for this node attrs
        if (data && data.attrs && data.attrs.slot) {
            delete data.attrs.slot
        }

        /* Check whether it is a named slot, if it is a named slot, it also requires that the subcomponent/function subcomponent render context consistent. * Does not keep the name of the slot when passing a named slot to a child of a child component. * /

        // named slots should only be respected if the vnode was rendered in the
        // same context.
        if((child.context === context || child.functionalContext === context) && data && data.slot ! =null
        ) {
            // Get the slot name
            const name = child.data.slot
            // If slots[name] does not exist, initialize to an empty array
            const slot = (slots[name] || (slots[name] = []))

            // Handle slots in the form of template for the parent component
            // If it is a tempalte element, add the children of template to the array
            if (child.tag === 'template') {
                slot.push.apply(slot, child.children)
            } else {
                slot.push(child)
            }
        } else {
            // Returns an anonymous default slot VNode array
            defaultSlot.push(child)
        }
    }

    // ignore whitespace
    // Ignore slots that contain only whitespace
    if(! defaultSlot.every(isWhitespace)) { slots.default = defaultSlot }return slots
}
Copy the code

Here’s an example:

<div id="app"> <div> <layout> <h1 slot="header">{{title}}</h1> <p>{{msg}}</p> <p slot="footer">{{footer}}</p> </layout> </div> </div> Vue.component('Layout', { template: '<div class="container"> <header> <slot name="header"> Default header</slot> </header> <main> <slot> Default main</slot> </main> <footer> <slot name="footer"> Default footer</slot> </footer> </div> '}) new Vue({el: '#app', template: ', data: {title: 'Here's the title ', MSG :' here's the content ', footer:' Here's the footer'}})Copy the code

As you can see, resolveSlots takes the children and layout context of the layout component, div#app, as input parameters

After resolveSlots is parsed, the result returned is as follows and stored in vm.$slots

As you can see from the DOM number changes, the rendering process is essentially replacing

with the corresponding content by name, which will be explained in more detail in a later section

After rendering, the named '<slot>' in the original DOM will be replaced with the corresponding name tag, and the unnamed '<slot>' will be replaced with the default slot.Copy the code

Returning to initRender, the second core of this function:

if(process.env.NODE_ENV ! = ='production') {
    defineReactive(vm, '$attrs', parentData && parentData.attrs || emptyObject, () => { ! isUpdatingChildComponent && warn(`$attrs is readonly.`, vm)
    }, true)
    defineReactive(vm, '$listeners', options._parentListeners || emptyObject, () => { ! isUpdatingChildComponent && warn(`$listeners is readonly.`, vm)
    }, true)}else {
    defineReactive(vm, '$attrs', parentData && parentData.attrs || emptyObject, null.true)
    defineReactive(vm, '$listeners', options._parentListeners || emptyObject, null.true)}Copy the code

Here, the defineReactive method is called to make $attrs and $Listeners listeners.