The processing flow of slot content
Processing Flow of Slot Content
Vue3's slot mechanism allows components to receive template fragments as content and decide how to render this content within the component. The core logic of slot processing is divided into two stages: compilation and runtime. The compiler transforms slot content in templates into specific data structures, while the runtime renders based on these data.
Compilation Phase Processing
The template compiler generates special code structures when encountering slot content. For default slots:
<template>
<ChildComponent>
<div>Default slot content</div>
</ChildComponent>
</template>
The compiled render function produces a structure like this:
import { createVNode as _createVNode, openBlock as _openBlock } from "vue"
export function render(_ctx, _cache) {
return (_openBlock(),
_createVNode(ChildComponent, null, {
default: () => [_createVNode("div", null, "Default slot content")],
_: 1 /* STABLE */
}))
}
Named slots are handled slightly differently:
<template>
<ChildComponent>
<template #header>
<h1>Title</h1>
</template>
</ChildComponent>
</template>
The compilation result is:
{
header: () => [_createVNode("h1", null, "Title")],
_: 1 /* STABLE */
}
Scoped slots receive parameters:
<template>
<ChildComponent>
<template #item="props">
<span>{{ props.text }}</span>
</template>
</ChildComponent>
</template>
The compiled output generates a function with parameters:
{
item: (props) => [_createVNode("span", null, _toDisplayString(props.text), 1 /* TEXT */)],
_: 1 /* STABLE */
}
Runtime Initialization
Slot-related logic is processed during component instantiation, primarily in the setupComponent
function:
function setupComponent(instance) {
// Handle props
initProps(instance)
// Handle slots
initSlots(instance, instance.vnode.children)
// Execute setup
setupStatefulComponent(instance)
}
Core logic of the initSlots
function:
function initSlots(instance, children) {
if (instance.vnode.shapeFlag & ShapeFlags.SLOTS_CHILDREN) {
const slots = {}
for (const key in children) {
slots[key] = (props) => normalizeSlotValue(children[key](props))
}
instance.slots = slots
} else {
instance.slots = {
default: () => normalizeSlotValue(children)
}
}
}
Slot Rendering Process
When rendering slot content, the renderSlot
function is executed:
function renderSlot(slots, name, props = {}) {
const slot = slots[name]
if (slot) {
const content = slot(props)
return createVNode(Fragment, { key: props.key }, content)
}
}
Example of a component using slots internally:
<template>
<div class="container">
<header>
<slot name="header"></slot>
</header>
<main>
<slot :data="internalData"></slot>
</main>
</div>
</template>
Corresponding render function:
export function render(_ctx, _cache) {
return (_openBlock(),
_createVNode("div", { class: "container" }, [
_createVNode("header", null, [
_renderSlot(_ctx.$slots, "header")
]),
_createVNode("main", null, [
_renderSlot(_ctx.$slots, "default", { data: _ctx.internalData })
])
]))
}
Dynamic Slot Name Handling
Vue3 supports dynamic slot names:
<template>
<ChildComponent>
<template #[dynamicSlotName]>
Dynamic slot content
</template>
</ChildComponent>
</template>
The compilation generates dynamic properties:
const slots = {
[dynamicSlotName]: () => [_createVNode("div", null, "Dynamic slot content")],
_: 1 /* STABLE */
}
Runtime processing first computes the slot name:
function renderDynamicSlot(slots, name) {
const slotName = unref(name) // Handle reactive values
return renderSlot(slots, slotName)
}
Slot Performance Optimizations
Vue3 implements several optimizations for slots:
- Static Node Hoisting: Static slot content is hoisted externally to avoid repeated creation.
const _hoisted_1 = _createVNode("div", null, "Static content")
function render() {
return _renderSlot(_ctx.$slots, "default", () => [_hoisted_1])
}
- Slot Marking: The compiler marks stable slot content to avoid unnecessary updates.
{
default: () => [/* content */],
_: 1 /* STABLE */ | 2 /* DYNAMIC */
}
- Scoped Slot Caching: Intelligently determines whether updates are needed when scoped slot parameters change.
function renderSlotWithCache(slots, name, props, cache, index) {
const slot = slots[name]
const cached = cache[index]
if (cached && isSameProps(cached.props, props)) {
return cached.vnode
}
const vnode = slot(props)
cache[index] = { props, vnode }
return vnode
}
Advanced Slot Patterns
- Render Delegation: Delegates slot rendering logic to child components.
<template>
<DelegateRenderer :renderer="$slots.default" />
</template>
- Slot Forwarding: Uses
v-slots
syntactic sugar to forward slots.
<template>
<WrapperComponent>
<template v-for="(_, name) in $slots" #[name]="props">
<slot :name="name" v-bind="props"></slot>
</template>
</WrapperComponent>
</template>
- Conditional Slots: Renders different slot content based on conditions.
function renderConditionalSlot() {
return condition.value
? renderSlot(slots, 'true')
: renderSlot(slots, 'false')
}
Slots and the Composition API
Using slots in the setup function:
export default {
setup(props, { slots }) {
// Check if slot exists
const hasHeader = computed(() => !!slots.header)
// Process slot content
const processedSlots = computed(() => {
return slots.default?.() || []
})
return { hasHeader, processedSlots }
}
}
Dynamically modifying slot content:
const dynamicSlot = ref(null)
provide('dynamic-slot', {
register: (content) => dynamicSlot.value = content,
unregister: () => dynamicSlot.value = null
})
const slots = computed(() => ({
default: () => dynamicSlot.value ? [dynamicSlot.value] : []
}))
Slot Type System
Vue3 provides comprehensive type support for slots:
defineComponent({
slots: Object as PropType<{
default?: (props: { data: string }) => VNode[]
header?: () => VNode[]
}>,
setup(props, { slots }) {
// slots now has full type hints
}
})
Example of a generic slot component:
interface SlotProps<T> {
item: T
index: number
}
const List = <T,>(props: { items: T[] }) => {
return props.items.map((item, index) =>
slots.default?.({ item, index })
)
}
本站部分内容来自互联网,一切版权均归源网站或源作者所有。
如果侵犯了你的权益请来信告知我们删除。邮箱:cc@cccx.cn
上一篇:Props的解析与验证
下一篇:组件更新的触发条件