preface
The way the contents of the slot are passed
- Template slots
Passing content directly to the component tag is compiled by the Complie module, and vue adds an internal attribute _. This is an internal property, meaning that this is a slot object generated by the compiler, using the reserved property instead of vNode patchFlag, because it can be passed directly to child components in the manual rendering function, and the optimization tip needs to be retained in the slot object itself.
- The function of slot
This is a manual rendering function, the user can manually write a slot object, you can directly pass the child component, but need to pay attention to the key and value of the corresponding relationship, this is because the named slot has a corresponding relationship, if the key and value do not correspond, will cause the rendering error in the child component. When vue normalizes children(where children are slot objects), it adds a context instance _ctx to them (where compiled or normalized children have context instances).
- Function slot (
vnode
Array)
The third method is less commonly used and is not recommended in VUE, so it is briefly mentioned here. In this way, the base of the function slot is changed. Children is a vNode array instead of a slot function object
A user-written rendering function that passes a vNode array instead of a slot object is acceptable, but is not recommended by VUE. Instead, it prompts the user to use a slot object.
Tool function
Check the vNode array returned by the slot function. Return the correct vNode.
Slot identification
Give a brief description of SlotFlags, which is a stable Fragment and which is not.
In the first case, this is forwardedif the content in the template slot is
. Means that
is being forwarded to a child component. Whether the parent needs to update the child depends on the type of slot the parent itself receives. Child nodes must be optimized at runtime (in “normalizeChildren”) when they are created, as shown in the following example
<Comp>
<slot></slot>
</Comp>
Copy the code
The second case is when a slot references a range variable (V-for or external slot attributes) or a slot with a conditional structure (V-if, V-for). The parent needs to force the child to update because the slot does not fully capture its dependencies. This is DYMAMIC, as shown below:
<Comp>
<template v-if="counter===1" v-slot:default="slotProps"></template>
</Comp>
Copy the code
The third case is a STABLE Fragment, STABLE, which refers only to a STABLE slot for a slot item or context state. Slots can fully capture their own dependencies, so the parent does not need to force the child to update when passing. Here’s an example:
const App = {
template: `
{{counter}}
`.setup() {
const counter = ref(0)
function increment() {
counter.value++
}
return { counter, increment }
},
components: {
Comp
}
}
Copy the code
Initializing slots
SLOTS_CHILDREN = SLOTS_CHILDREN; SLOTS_CHILDREN = SLOTS_CHILDREN; SLOTS_CHILDREN = SLOTS_CHILDREN; To ensure that the specification normalizeVNodeSlots normalizes the user-specified VNode array.
Type is derived from the _ attribute in children, which means that the user is using the built-in
component, which is compiled only by the complie module, and is already compliant and can be placed directly into the slots attribute on the component instance. Alternatively, the user writes the slot function object. It’s not compiled by the Complie module, it’s not normalized by the _ attribute, it’s normalized by normalizeObjectSlots.
Standardized slot
There are many ways to transfer the contents of slots, and they need to be unified into one form for easy processing. We don’t need to normalize compiled content passing through component tags, we just need to normalize user-written rendering functions and passed vNode arrays.
We a variety of situations to analysis, the first look at the user manual given slot function object, in this case will perform normalizeObjectSlots, pass to users slot objects for the standardization of the unity, traverse the slot in the object properties, first to make sure that this is not the vue retention properties: _, _CTx, or one of $stable.
If it is a function, it will follow the normalizeSlot normalizeSlot slot function, which will be wrapped inside the user-passed function, more on this later, and finally mark this as a slot that is not compiled by Complie.
NormalizeSlotValue can be used to normalize the vnode passed by the user. It can be passed as a single Vnode or as an array of VNodes. It is ultimately handed over to normalizeVNode to normalize vNodes. Finally, put it as a function into the slots attribute on the instance.
The second case is similar to a variant of the render function slot, passing a vNode array instead of a slot function, normalizing through normalizeVNodeSlots, normalizing through the process of normalizeSlotValue, and ending up with a slot function that generates a Defulat slot.
Packing slot function
Slot function, and not directly executed users transfer function, even through the compilation of packaging, to the withCtx function (position at runtime – core/SRC/componentRenderContext. Ts)
This function needs to pass a function (slot function) and the render context CTX and an isNonScopedSlot to distinguish between a scoped slot and a slot. CTX must be present, otherwise it will end up with a renderFnWithContext function. One is to find the corresponding render context during slot function execution, and the second is to suspend block tracking, because when the user manually calls the compiled slot function, block tracking can be corrupted.
RenderFnWithContext is given several attributes, _n indicating whether it’s wrapped or not, and if it’s true then it exits. _c means compiled, _d means block tracking is disabled by default, and _ns means this is a scoped slot? RenderFnWithContext is returned.
Parse the contents of the slot
Instead of executing the wrapped slot function directly, a VUE private function called renderSlot is used to execute the slot contents. Position at runtime – core/SRC/helpers/renderSlot ts, assuming components such as accept slot content below,
<slot :attribute="' I am something that goes out. '"> I am the alternate content </slot>Copy the code
The renderSlot function takes five arguments. The first is the slot function object slots on the instance. The second is the name of the slot. The fifth one I suspect is whether styles use a pseudo-class selector: Slotted.
We start with the custom element, isCE is the identification that the current instance is an instance of the custom element, and return VNode directly, end. If it is not a custom element, go through the following process to extract the slot function with the corresponding name and assign it to slot.
At this point, it means that this is a template-based call, not a user call, so don’t worry about disrupting the block trace, and enable the block trace. In VUE, the slot content is a block, and here a block is opened.
Now pass the props to the slot function to get the vNode, and pass it to ensureValidVNode to check the vNode. If slot is present, the validSlotContent is the correct VNode to check. Create a VNode block of type Fragment
If the props has a key, name is used as the key. If validSlotContent is false (there is no correct VNode), the fallback content replaces it. STABLE is a STABLE Fragment as long as validSlotContent is true and slotFlags. STABLE is a STABLE Fragment or BAIL.
Verify the slot’s style scope ID, disable block tracing, and return the resulting VNode block. Only render and normal vNode initialization mount is no different, still go patch.
Update slot content
Updating the contents of a slot is actually very simple. It only needs to re-execute the slot function to get a new VNode block, which can be handed over to Patch for DOM diff. It is also possible to force updates when the parent component is updated.
The render function receives changes to the contents of the slot
In v3, there are some changes to this.$slots. The first figure is v2 $slots, and the second figure is V3 $slots. In v3, the way to get slot content templates from components has also been changed. V2 just needs to go to vm.$slots.default to get the vNode of the default slot. This.$slots.default() is required to get the vNode of the default slot.
This is the result of the this.$slots.default() operation. It looks exactly the same as the result of the v2 operation. There is no way to pass data from a component into a slot, and V3 provides a way to pass data to a slot in a render function. Here’s an example:
The previous condition of this example is to use the h function to render the structure. The App component and Comp component are parent-child components. The slot contents are passed in the render function and need to be passed in the third function of function H, where the third parameter is no longer an array but an object corresponding to the named slot.
In the example above I specified the contents of the default slot, which is a function that returns vNodes. In the Comp component you can use this.$slots.default() to get the returned VNode. If I pass a parameter when execution, then the App in the component slot function, I can pick up in the Comp in slot function components in the transmission of data, can set up multiple parameters accept, deconstruction parameters to get one of them, also can use to get all the remaining operator, and it also formed a closure. This way we can write rendering functions, intercept the data, and then decide how to render.
conclusion
Slot initialization and update the analysis, in the process, know how to slot the vue is content to render to the specified location, with the help of multiple slots, how to find rendering position by name, how to deal with the user content, scope of slot data is how to use, transfer and can better use the slot in the development.
If there is a lack of or is wrong, welcome to guide and correct in the comment area, thank you