@@ -3,9 +3,18 @@ import {
33 VNode ,
44 VNodeNormalizedChildren ,
55 normalizeVNode ,
6- VNodeChild
6+ VNodeChild ,
7+ InternalObjectSymbol
78} from './vnode'
8- import { isArray , isFunction , EMPTY_OBJ , ShapeFlags } from '@vue/shared'
9+ import {
10+ isArray ,
11+ isFunction ,
12+ EMPTY_OBJ ,
13+ ShapeFlags ,
14+ PatchFlags ,
15+ extend ,
16+ def
17+ } from '@vue/shared'
918import { warn } from './warning'
1019import { isKeepAlive } from './components/KeepAlive'
1120import { withCtx } from './helpers/withRenderContext'
@@ -25,10 +34,12 @@ export type RawSlots = {
2534 // internal, for tracking slot owner instance. This is attached during
2635 // normalizeChildren when the component vnode is created.
2736 _ctx ?: ComponentInternalInstance | null
28- // internal, indicates compiler generated slots = can skip normalization
37+ // internal, indicates compiler generated slots
2938 _ ?: 1
3039}
3140
41+ const isInternalKey = ( key : string ) => key [ 0 ] === '_' || key === '$stable'
42+
3243const normalizeSlotValue = ( value : unknown ) : VNode [ ] =>
3344 isArray ( value )
3445 ? value . map ( normalizeVNode )
@@ -50,46 +61,94 @@ const normalizeSlot = (
5061 return normalizeSlotValue ( rawSlot ( props ) )
5162 } , ctx )
5263
53- export function resolveSlots (
64+ const normalizeObjectSlots = ( rawSlots : RawSlots , slots : InternalSlots ) => {
65+ const ctx = rawSlots . _ctx
66+ for ( const key in rawSlots ) {
67+ if ( isInternalKey ( key ) ) continue
68+ const value = rawSlots [ key ]
69+ if ( isFunction ( value ) ) {
70+ slots [ key ] = normalizeSlot ( key , value , ctx )
71+ } else if ( value != null ) {
72+ if ( __DEV__ ) {
73+ warn (
74+ `Non-function value encountered for slot "${ key } ". ` +
75+ `Prefer function slots for better performance.`
76+ )
77+ }
78+ const normalized = normalizeSlotValue ( value )
79+ slots [ key ] = ( ) => normalized
80+ }
81+ }
82+ }
83+
84+ const normalizeVNodeSlots = (
5485 instance : ComponentInternalInstance ,
5586 children : VNodeNormalizedChildren
56- ) {
57- let slots : InternalSlots | void
87+ ) => {
88+ if ( __DEV__ && ! isKeepAlive ( instance . vnode ) ) {
89+ warn (
90+ `Non-function value encountered for default slot. ` +
91+ `Prefer function slots for better performance.`
92+ )
93+ }
94+ const normalized = normalizeSlotValue ( children )
95+ instance . slots . default = ( ) => normalized
96+ }
97+
98+ export const initSlots = (
99+ instance : ComponentInternalInstance ,
100+ children : VNodeNormalizedChildren
101+ ) => {
58102 if ( instance . vnode . shapeFlag & ShapeFlags . SLOTS_CHILDREN ) {
59- const rawSlots = children as RawSlots
60- if ( rawSlots . _ === 1 ) {
61- // pre-normalized slots object generated by compiler
62- slots = children as Slots
103+ if ( ( children as RawSlots ) . _ === 1 ) {
104+ instance . slots = children as InternalSlots
63105 } else {
64- slots = { }
65- const ctx = rawSlots . _ctx
66- for ( const key in rawSlots ) {
67- if ( key === '$stable' || key === '_ctx' ) continue
68- const value = rawSlots [ key ]
69- if ( isFunction ( value ) ) {
70- slots [ key ] = normalizeSlot ( key , value , ctx )
71- } else if ( value != null ) {
72- if ( __DEV__ ) {
73- warn (
74- `Non-function value encountered for slot "${ key } ". ` +
75- `Prefer function slots for better performance.`
76- )
77- }
78- const normalized = normalizeSlotValue ( value )
79- slots [ key ] = ( ) => normalized
80- }
106+ normalizeObjectSlots ( children as RawSlots , ( instance . slots = { } ) )
107+ }
108+ } else {
109+ instance . slots = { }
110+ if ( children ) {
111+ normalizeVNodeSlots ( instance , children )
112+ }
113+ }
114+ def ( instance . slots , InternalObjectSymbol , true )
115+ }
116+
117+ export const updateSlots = (
118+ instance : ComponentInternalInstance ,
119+ children : VNodeNormalizedChildren
120+ ) => {
121+ const { vnode, slots } = instance
122+ let needDeletionCheck = true
123+ let deletionComparisonTarget = EMPTY_OBJ
124+ if ( vnode . shapeFlag & ShapeFlags . SLOTS_CHILDREN ) {
125+ if ( ( children as RawSlots ) . _ === 1 ) {
126+ if ( ! ( vnode . patchFlag & PatchFlags . DYNAMIC_SLOTS ) ) {
127+ // compiled AND static. this means we can skip removal of potential
128+ // stale slots
129+ needDeletionCheck = false
130+ }
131+ // HMR force update
132+ if ( __DEV__ && instance . parent && instance . parent . renderUpdated ) {
133+ extend ( slots , children as Slots )
81134 }
135+ } else {
136+ needDeletionCheck = ! ( children as RawSlots ) . $stable
137+ normalizeObjectSlots ( children as RawSlots , slots )
82138 }
139+ deletionComparisonTarget = children as RawSlots
83140 } else if ( children ) {
84141 // non slot object children (direct value) passed to a component
85- if ( __DEV__ && ! isKeepAlive ( instance . vnode ) ) {
86- warn (
87- `Non-function value encountered for default slot. ` +
88- `Prefer function slots for better performance.`
89- )
142+ normalizeVNodeSlots ( instance , children )
143+ deletionComparisonTarget = { default : 1 }
144+ }
145+
146+ // delete stale slots
147+ if ( needDeletionCheck ) {
148+ for ( const key in slots ) {
149+ if ( ! isInternalKey ( key ) && ! ( key in deletionComparisonTarget ) ) {
150+ delete slots [ key ]
151+ }
90152 }
91- const normalized = normalizeSlotValue ( children )
92- slots = { default : ( ) => normalized }
93153 }
94- instance . slots = slots || EMPTY_OBJ
95154}
0 commit comments