vue3-yuanma/packages/runtime-core/src/componentSlots.ts

96 lines
2.9 KiB
TypeScript

import { ComponentInternalInstance, currentInstance } from './component'
import {
VNode,
VNodeNormalizedChildren,
normalizeVNode,
VNodeChild
} from './vnode'
import { isArray, isFunction, EMPTY_OBJ, ShapeFlags } from '@vue/shared'
import { warn } from './warning'
import { isKeepAlive } from './components/KeepAlive'
import { withCtx } from './helpers/withRenderContext'
export type Slot = (...args: any[]) => VNode[]
export type InternalSlots = {
[name: string]: Slot | undefined
}
export type Slots = Readonly<InternalSlots>
export type RawSlots = {
[name: string]: unknown
// manual render fn hint to skip forced children updates
$stable?: boolean
// internal, for tracking slot owner instance. This is attached during
// normalizeChildren when the component vnode is created.
_ctx?: ComponentInternalInstance | null
// internal, indicates compiler generated slots = can skip normalization
_?: 1
}
const normalizeSlotValue = (value: unknown): VNode[] =>
isArray(value)
? value.map(normalizeVNode)
: [normalizeVNode(value as VNodeChild)]
const normalizeSlot = (
key: string,
rawSlot: Function,
ctx: ComponentInternalInstance | null | undefined
): Slot =>
withCtx((props: any) => {
if (__DEV__ && currentInstance != null) {
warn(
`Slot "${key}" invoked outside of the render function: ` +
`this will not track dependencies used in the slot. ` +
`Invoke the slot function inside the render function instead.`
)
}
return normalizeSlotValue(rawSlot(props))
}, ctx)
export function resolveSlots(
instance: ComponentInternalInstance,
children: VNodeNormalizedChildren
) {
let slots: InternalSlots | void
if (instance.vnode.shapeFlag & ShapeFlags.SLOTS_CHILDREN) {
const rawSlots = children as RawSlots
if (rawSlots._ === 1) {
// pre-normalized slots object generated by compiler
slots = children as Slots
} else {
slots = {}
const ctx = rawSlots._ctx
for (const key in rawSlots) {
if (key === '$stable' || key === '_ctx') continue
const value = rawSlots[key]
if (isFunction(value)) {
slots[key] = normalizeSlot(key, value, ctx)
} else if (value != null) {
if (__DEV__) {
warn(
`Non-function value encountered for slot "${key}". ` +
`Prefer function slots for better performance.`
)
}
const normalized = normalizeSlotValue(value)
slots[key] = () => normalized
}
}
}
} else if (children !== null) {
// non slot object children (direct value) passed to a component
if (__DEV__ && !isKeepAlive(instance.vnode)) {
warn(
`Non-function value encountered for default slot. ` +
`Prefer function slots for better performance.`
)
}
const normalized = normalizeSlotValue(children)
slots = { default: () => normalized }
}
instance.slots = slots || EMPTY_OBJ
}