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 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 }