@@ -3,9 +3,18 @@ import {
3
3
VNode ,
4
4
VNodeNormalizedChildren ,
5
5
normalizeVNode ,
6
- VNodeChild
6
+ VNodeChild ,
7
+ InternalObjectSymbol
7
8
} 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'
9
18
import { warn } from './warning'
10
19
import { isKeepAlive } from './components/KeepAlive'
11
20
import { withCtx } from './helpers/withRenderContext'
@@ -25,10 +34,12 @@ export type RawSlots = {
25
34
// internal, for tracking slot owner instance. This is attached during
26
35
// normalizeChildren when the component vnode is created.
27
36
_ctx ?: ComponentInternalInstance | null
28
- // internal, indicates compiler generated slots = can skip normalization
37
+ // internal, indicates compiler generated slots
29
38
_ ?: 1
30
39
}
31
40
41
+ const isInternalKey = ( key : string ) => key [ 0 ] === '_' || key === '$stable'
42
+
32
43
const normalizeSlotValue = ( value : unknown ) : VNode [ ] =>
33
44
isArray ( value )
34
45
? value . map ( normalizeVNode )
@@ -50,46 +61,94 @@ const normalizeSlot = (
50
61
return normalizeSlotValue ( rawSlot ( props ) )
51
62
} , ctx )
52
63
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 = (
54
85
instance : ComponentInternalInstance ,
55
86
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
+ ) => {
58
102
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
63
105
} 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 )
81
134
}
135
+ } else {
136
+ needDeletionCheck = ! ( children as RawSlots ) . $stable
137
+ normalizeObjectSlots ( children as RawSlots , slots )
82
138
}
139
+ deletionComparisonTarget = children as RawSlots
83
140
} else if ( children ) {
84
141
// 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
+ }
90
152
}
91
- const normalized = normalizeSlotValue ( children )
92
- slots = { default : ( ) => normalized }
93
153
}
94
- instance . slots = slots || EMPTY_OBJ
95
154
}
0 commit comments