1- import { EMPTY_OBJ , NO , hasOwn , isArray , isFunction } from '@vue/shared'
1+ import { EMPTY_OBJ , NO , isArray , isFunction } from '@vue/shared'
22import { type Block , type BlockFn , DynamicFragment } from './block'
33import {
44 type RawProps ,
55 getAttrFromRawProps ,
66 getKeysFromRawProps ,
77 hasAttrFromRawProps ,
88} from './componentProps'
9- import { currentInstance } from '@vue/runtime-core'
9+ import { currentInstance , shallowReactive } from '@vue/runtime-core'
1010import type { LooseRawProps , VaporComponentInstance } from './component'
1111import { renderEffect } from './renderEffect'
12+ import { enableTracking , pauseTracking } from '@vue/reactivity'
1213
1314export type RawSlots = Record < string , Slot > & {
1415 $ ?: DynamicSlotSource [ ]
16+ $levelQueues ?: SlotsLevelQueues
1517}
1618
1719export type StaticSlots = Record < string , Slot >
@@ -23,7 +25,10 @@ export type DynamicSlotSource = StaticSlots | DynamicSlotFn
2325
2426export const dynamicSlotsProxyHandlers : ProxyHandler < RawSlots > = {
2527 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+ } ,
2732 getOwnPropertyDescriptor ( target , key : string ) {
2833 const slot = getSlot ( target , key )
2934 if ( slot ) {
@@ -35,23 +40,8 @@ export const dynamicSlotsProxyHandlers: ProxyHandler<RawSlots> = {
3540 }
3641 } ,
3742 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 ( ) ]
5545 } ,
5646 set : NO ,
5747 deleteProperty : NO ,
@@ -61,30 +51,11 @@ export function getSlot(
6151 target : RawSlots ,
6252 key : string ,
6353) : ( 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 . $ ) {
8655 return target [ key ]
8756 }
57+ const levelSlots = ensureLevelQueues ( target )
58+ return levelSlots . getSlot ( key )
8859}
8960
9061const dynamicSlotsPropsProxyHandlers : ProxyHandler < RawProps > = {
@@ -151,3 +122,90 @@ export function createSlot(
151122
152123// TODO
153124export 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