1
- import { EMPTY_OBJ , NO , hasOwn , isArray , isFunction } from '@vue/shared'
1
+ import { EMPTY_OBJ , NO , isArray , isFunction } from '@vue/shared'
2
2
import { type Block , type BlockFn , DynamicFragment } from './block'
3
3
import {
4
4
type RawProps ,
5
5
getAttrFromRawProps ,
6
6
getKeysFromRawProps ,
7
7
hasAttrFromRawProps ,
8
8
} from './componentProps'
9
- import { currentInstance } from '@vue/runtime-core'
9
+ import { currentInstance , shallowReactive } from '@vue/runtime-core'
10
10
import type { LooseRawProps , VaporComponentInstance } from './component'
11
11
import { renderEffect } from './renderEffect'
12
+ import { enableTracking , pauseTracking } from '@vue/reactivity'
12
13
13
14
export type RawSlots = Record < string , Slot > & {
14
15
$ ?: DynamicSlotSource [ ]
16
+ $levelQueues ?: SlotsLevelQueues
15
17
}
16
18
17
19
export type StaticSlots = Record < string , Slot >
@@ -23,7 +25,10 @@ export type DynamicSlotSource = StaticSlots | DynamicSlotFn
23
25
24
26
export const dynamicSlotsProxyHandlers : ProxyHandler < RawSlots > = {
25
27
get : getSlot ,
26
- has : ( target , key : string ) => ! ! getSlot ( target , key ) ,
28
+ has : ( target , key : string ) => {
29
+ const levelSlots = ensureLevelQueues ( target )
30
+ return levelSlots . queues . has ( key )
31
+ } ,
27
32
getOwnPropertyDescriptor ( target , key : string ) {
28
33
const slot = getSlot ( target , key )
29
34
if ( slot ) {
@@ -35,23 +40,8 @@ export const dynamicSlotsProxyHandlers: ProxyHandler<RawSlots> = {
35
40
}
36
41
} ,
37
42
ownKeys ( target ) {
38
- const keys = Object . keys ( target )
39
- const dynamicSources = target . $
40
- if ( dynamicSources ) {
41
- for ( const source of dynamicSources ) {
42
- if ( isFunction ( source ) ) {
43
- const slot = source ( )
44
- if ( isArray ( slot ) ) {
45
- for ( const s of slot ) keys . push ( s . name )
46
- } else {
47
- keys . push ( slot . name )
48
- }
49
- } else {
50
- keys . push ( ...Object . keys ( source ) )
51
- }
52
- }
53
- }
54
- return keys
43
+ const levelSlots = ensureLevelQueues ( target )
44
+ return [ ...levelSlots . queues . keys ( ) ]
55
45
} ,
56
46
set : NO ,
57
47
deleteProperty : NO ,
@@ -61,30 +51,11 @@ export function getSlot(
61
51
target : RawSlots ,
62
52
key : string ,
63
53
) : ( Slot & { _bound ?: Slot } ) | undefined {
64
- if ( key === '$' ) return
65
- const dynamicSources = target . $
66
- if ( dynamicSources ) {
67
- let i = dynamicSources . length
68
- let source
69
- while ( i -- ) {
70
- source = dynamicSources [ i ]
71
- if ( isFunction ( source ) ) {
72
- const slot = source ( )
73
- if ( isArray ( slot ) ) {
74
- for ( const s of slot ) {
75
- if ( s . name === key ) return s . fn
76
- }
77
- } else if ( slot . name === key ) {
78
- return slot . fn
79
- }
80
- } else if ( hasOwn ( source , key ) ) {
81
- return source [ key ]
82
- }
83
- }
84
- }
85
- if ( hasOwn ( target , key ) ) {
54
+ if ( ! target . $ ) {
86
55
return target [ key ]
87
56
}
57
+ const levelSlots = ensureLevelQueues ( target )
58
+ return levelSlots . getSlot ( key )
88
59
}
89
60
90
61
const dynamicSlotsPropsProxyHandlers : ProxyHandler < RawProps > = {
@@ -151,3 +122,90 @@ export function createSlot(
151
122
152
123
// TODO
153
124
export function createForSlots ( ) : any { }
125
+
126
+ export function ensureLevelQueues ( rawSlots : RawSlots ) : SlotsLevelQueues {
127
+ if ( ! rawSlots . $levelQueues ) {
128
+ rawSlots . $levelQueues = new SlotsLevelQueues ( rawSlots )
129
+ }
130
+ return rawSlots . $levelQueues
131
+ }
132
+
133
+ export type SlotLevelItem = [ number , BlockFn ]
134
+
135
+ export class SlotsLevelQueues {
136
+ queues : Map < string , SlotLevelItem [ ] > = shallowReactive ( new Map ( ) )
137
+
138
+ constructor ( rawSlots : RawSlots ) {
139
+ this . setupLevel ( rawSlots )
140
+ }
141
+
142
+ public getSlot ( name : string ) : BlockFn | undefined {
143
+ const queue = this . queues . get ( name )
144
+ if ( queue && queue . length ) {
145
+ return queue [ 0 ] [ 1 ]
146
+ }
147
+ }
148
+
149
+ private setupLevel ( rawSlots : RawSlots ) {
150
+ const dynamicSources = rawSlots . $
151
+ if ( dynamicSources ) {
152
+ let index = 0
153
+ for ( const source of dynamicSources ) {
154
+ const level = index ++
155
+ const isDynamicSlot = isFunction ( source )
156
+ if ( isDynamicSlot ) {
157
+ renderEffect ( ( ) => {
158
+ const slot = source ( )
159
+ this . cleanupLevel ( level )
160
+ if ( isArray ( slot ) ) {
161
+ for ( const s of slot ) {
162
+ this . registerLevelSlot ( s . name , [ level , s . fn ] )
163
+ }
164
+ } else {
165
+ this . registerLevelSlot ( slot . name , [ level , slot . fn ] )
166
+ }
167
+ } )
168
+ } else {
169
+ for ( const name of Object . keys ( source ) ) {
170
+ this . registerLevelSlot ( name , [ level , source [ name ] ] )
171
+ }
172
+ }
173
+ }
174
+ }
175
+ }
176
+
177
+ private registerLevelSlot ( name : string , levelSlot : SlotLevelItem ) {
178
+ const queue = this . queues . get ( name ) || this . queues . set ( name , [ ] ) . get ( name ) !
179
+ this . insertLevelSlot ( queue , levelSlot )
180
+ }
181
+
182
+ private insertLevelSlot ( queue : SlotLevelItem [ ] , levelSlot : SlotLevelItem ) {
183
+ // binary insert, keep the queue sorted by level ascending
184
+ let start = 0
185
+ let end = queue . length
186
+ while ( start < end ) {
187
+ const mid = ( start + end ) >>> 1
188
+ if ( queue [ mid ] [ 0 ] < levelSlot [ 0 ] ) {
189
+ start = mid + 1
190
+ } else {
191
+ end = mid
192
+ }
193
+ }
194
+ queue . splice ( start , 0 , levelSlot )
195
+ }
196
+
197
+ private cleanupLevel ( level : number ) {
198
+ pauseTracking ( )
199
+ for ( const [ name , queue ] of this . queues . entries ( ) ) {
200
+ for ( let i = 0 ; i < queue . length ; i ++ ) {
201
+ if ( queue [ i ] [ 0 ] === level ) {
202
+ queue . splice ( i , 1 )
203
+ if ( queue . length === 0 ) {
204
+ this . queues . delete ( name )
205
+ }
206
+ }
207
+ }
208
+ }
209
+ enableTracking ( )
210
+ }
211
+ }
0 commit comments