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.