import { ComponentInternalInstance , currentInstance } from './component' ; import { VNode , VNodeNormalizedChildren , normalizeVNode , VNodeChild , InternalObjectKey } from './vnode' ; import { isArray , isFunction , EMPTY_OBJ , ShapeFlags , extend , def , SlotFlags } from '@vue/shared' ; import { warn } from './warning' ; import { isKeepAlive } from './components/KeepAlive' ; import { ContextualRenderFn , withCtx } from './componentRenderContext' ; import { isHmrUpdating } from './hmr' ; import { DeprecationTypes , isCompatEnabled } from './compat/compatConfig' ; import { toRaw } from '@vue/reactivity' ; export type Slot = ( ... args : any [ ] ) => VNode [ ] ; export type InternalSlots = { [ name ] : Slot | undefined ; } ; export type Slots = Readonly < InternalSlots > ; export type RawSlots = { [ name ] : unknown ; $stable ? : boolean ; internal _ctx ? : ComponentInternalInstance | null ; internal _ ? : SlotFlags ; } ; const isInternalKey = ( key ) => key [ 0 ] === '_' || key === '$stable' ; const normalizeSlotValue = ( value ) : VNode [ ] => isArray ( value ) ? value . map ( normalizeVNode ) : [ normalizeVNode ( value as VNodeChild ) ] ; const normalizeSlot = ( key , rawSlot , ctx ) : Slot => { const normalized = withCtx ( ( ... args ) => { if ( __DEV__ && currentInstance ) { 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 ( ... args ) ) ; } , ctx ) as Slot ; ( normalized as ContextualRenderFn ) . _c = false ; return normalized ; } ; const normalizeObjectSlots = ( rawSlots , slots , instance ) => { const ctx = rawSlots . _ctx ; for ( const key in rawSlots ) { if ( isInternalKey ( key ) ) continue ; const value = rawSlots [ key ] ; if ( isFunction ( value ) ) { slots [ key ] = normalizeSlot ( key , value , ctx ) ; } else if ( value != null ) { if ( __DEV__ && ! ( __COMPAT__ && isCompatEnabled ( DeprecationTypes . RENDER_FUNCTION , instance ) ) ) { warn ( `Non-function value encountered for slot "${ key }". ` + `Prefer function slots for better performance.` ) ; } const normalized = normalizeSlotValue ( value ) ; slots [ key ] = ( ) => normalized ; } } } ; const normalizeVNodeSlots = ( instance , children ) => { if ( __DEV__ && ! isKeepAlive ( instance . vnode ) && ! ( __COMPAT__ && isCompatEnabled ( DeprecationTypes . RENDER_FUNCTION , instance ) ) ) { warn ( `Non-function value encountered for default slot. ` + `Prefer function slots for better performance.` ) ; } const normalized = normalizeSlotValue ( children ) ; instance . slots . default = ( ) => normalized ; } ; export const initSlots = ( instance , children ) => { if ( instance . vnode . shapeFlag & ShapeFlags . SLOTS_CHILDREN ) { const type = ( children as RawSlots ) . _ ; if ( type ) { instance . slots = toRaw ( children as InternalSlots ) ; def ( children as InternalSlots , '_' , type ) ; } else { normalizeObjectSlots ( children as RawSlots , ( instance . slots = { } ) , instance ) ; } } else { instance . slots = { } ; if ( children ) { normalizeVNodeSlots ( instance , children ) ; } } def ( instance . slots , InternalObjectKey , 1 ) ; } ; export const updateSlots = ( instance , children , optimized ) => { const { vnode , slots } = instance ; let needDeletionCheck = true ; let deletionComparisonTarget = EMPTY_OBJ ; if ( vnode . shapeFlag & ShapeFlags . SLOTS_CHILDREN ) { const type = ( children as RawSlots ) . _ ; if ( type ) { if ( __DEV__ && isHmrUpdating ) { extend ( slots , children as Slots ) ; } else if ( optimized && type === SlotFlags . STABLE ) { needDeletionCheck = false ; } else { extend ( slots , children as Slots ) ; if ( ! optimized && type === SlotFlags . STABLE ) { delete slots . _ ; } } } else { needDeletionCheck = ! ( children as RawSlots ) . $stable ; normalizeObjectSlots ( children as RawSlots , slots , instance ) ; } deletionComparisonTarget = children as RawSlots ; } else if ( children ) { normalizeVNodeSlots ( instance , children ) ; deletionComparisonTarget = { default : 1 } ; } if ( needDeletionCheck ) { for ( const key in slots ) { if ( ! isInternalKey ( key ) && ! ( key in deletionComparisonTarget ) ) { delete slots [ key ] ; } } } } ;