96 lines
2.9 KiB
TypeScript
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
|
|
}
|