This is the 11th day of my participation in the Gwen Challenge in November. Check out the details: The last Gwen Challenge in 2021

Another important concept of Vue components is slots, which allow you to combine components in a way that is different from the strict parent-child relationship. Slots give you an outlet to put content in a new location or make components more generic. In this article, according to the ordinary slot, named slot, and then to the scope of the slot, gradually in-depth internal implementation principle, there are not familiar with the use of slot, you can first refer to the official website of [slot](URL)[introduction].Copy the code

Ordinary slot

var child = { template: `<div class="child"><slot></slot></div>` } var vm = new Vue({ el: '#app', components: { child }, template: '<div id="app"><child>test</child></div>'}) There is no special difference between the processing of template parsing and the generation of the 'render' function, so we will not expand the analysis here. The next step is the render function generating the Vnode, which encounters the child placeholder node (child) and thus creates the child Vnode for the child component. CreateComponent performs the process of creating a child placeholder node 'Vnode'. We focus on the generation of the final 'Vnode' code.Copy the code

Component mounting principle

The principle of slot, through the whole component system compilation to render process, so first need to review the component related compilation rendering process, briefly summarize the following points:

  1. Mount the instance from the root instance, if handwrittenrenderFunction, then directly into$mountMounting process.
  2. onlytemplateTemplates need to be parsed, which is divided into two stages: one is to parse the template asASTTree, another is to generate execution code for different platforms, for examplerenderFunction.
  3. $mountThe process is also divided into two steps, the first step is torenderFunction to generateVnodeTree. If it encounters a child component, the child component will become a child componentvue-componet-fortagMark, the other step is to putVnodeRender as a real DOM node.
  4. During the process of creating a real node, if a child placeholder component is encountered, the child component is instantiated, which goes back to the first step of the process.

Parent component handling

Function createComponent (Ctor, // subclass constructor data, context, // vm instance children, // Parent component needs to distribute content tag // child component placeholder){··· // create child vNode, Var Vnode = new Vnode (("vue-component-" + (Ctor. Cid) + (name? ("-" + name) : '')), data, undefined, undefined, undefined, context, { Ctor: Ctor, propsData: propsData, listeners: listeners, tag: tag, children: children }, asyncFactory ); } / / Vnode constructor var Vnode = function Vnode (tag, the data, the children, text, elm, context, componentOptions, asyncFactory) {... this.componentOptions = componentOptions; // Options related to child components}Copy the code

The fourth argument received by the createComponent function, children, is what the parent component needs to distribute. During the creation of a child Vnode, the componentOptions configuration is passed into the Vnode constructor. Finally, the content that the parent component needs to distribute in Vnode exists in the form of the componentOptions property, which is the first step in slot analysis.

Subcomponent flow

The final stage of the parent component is to render the Vnode as a real DOM node, during which the child Vnode is instantiated first and a series of child component rendering processes are performed. The child initializes by calling the _init method and, unlike the parent, calls the initInternalComponent method to get the parent’s configuration information and assign its own configuration options to the child.

Prototype._init = function(options) {if (options && options._isComponent) {initInternalComponent(VM, options); } initRender(vm) } function initInternalComponent (vm, options) { var opts = vm.$options = Object.create(vm.constructor.options); var parentVnode = options._parentVnode; opts.parent = options.parent; opts._parentVnode = parentVnode; / / componentOptions as child vnode records information about the var vnodeComponentOptions = parentVnode.com ponentOptions; opts.propsData = vnodeComponentOptions.propsData; opts._parentListeners = vnodeComponentOptions.listeners; / / parent components need to distribute the content of the assignment to grant options to configure _renderChildren opts. _renderChildren = vnodeComponentOptions. Children; opts._componentTag = vnodeComponentOptions.tag; if (options.render) { opts.render = options.render; opts.staticRenderFns = options.staticRenderFns; }}Copy the code

$options._renderChildren: $options. renderChildren: $options. renderchildren: $options. renderchildren: $options. renderchildren: $options. renderchildren: $options. renderchildren: $options. renderchildren: $options. renderchildren: $options. renderchildren The instantiation of the child component then goes to the initRender stage, where the configured _renderChildren property is normalized and assigned to the $slot property on the child instance, which is the focus of step 3.

Function initRender(vm) {··· vm.$slots = resolveSlots(options._renderChildren, renderContext); // $slots gets the _renderChildren of the child placeholder node. } function resolveSlots (children,context) {// Children are vNodes that the parent component needs to distribute to the children. 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. After analyzing the if ((child. The context = = = the context | | child. FnContext = = = the context) && data & & data. Slot! = null ) { var name = data.slot; var slot = (slots[name] || (slots[name] = [])); if (child.tag === 'template') { slot.push.apply(slot, child.children || []); } else { slot.push(child); }} else {/ / common slot, the focus of the core logic is constructed {default: [children]} object returns (slots. The default | | (slots. Default = [])), push (child); } } return slots }Copy the code

Which core in ordinary slot processing logic (slots. The default | | (slots. Default = [])). Push (child); That is, it is assigned to the default property as an array and stored in the child component instance as the $slot property.

The child component then goes through the mount process, again from the Template to the render function to the Vnode to render the actual DOM. During the AST phase, slot tags are processed in the same way as other normal tags, except that the AST generates the render function, and the _t function is used to wrap slot tags. This is the fourth critical step and the final render function for the subcomponent is:

"with(this){return _c('div',{staticClass:"child"},[_t("default")],2)}"

Copy the code

The fifth step is to render the child components as VNodes. The render function executes the _t() function, short for renderSlot, which replaces the distribution in the Vnode tree. See the implementation logic. At this point, a complete and simple slot flow analysis is complete.