Skip to content

Commit adf8969

Browse files
committed
wip(runtime-vapor): try to fix level slots mess up
1 parent b2714ce commit adf8969

File tree

1 file changed

+100
-42
lines changed

1 file changed

+100
-42
lines changed

packages/runtime-vapor/src/componentSlots.ts

+100-42
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,19 @@
1-
import { EMPTY_OBJ, NO, hasOwn, isArray, isFunction } from '@vue/shared'
1+
import { EMPTY_OBJ, NO, isArray, isFunction } from '@vue/shared'
22
import { type Block, type BlockFn, DynamicFragment } from './block'
33
import {
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'
1010
import type { LooseRawProps, VaporComponentInstance } from './component'
1111
import { renderEffect } from './renderEffect'
12+
import { enableTracking, pauseTracking } from '@vue/reactivity'
1213

1314
export type RawSlots = Record<string, Slot> & {
1415
$?: DynamicSlotSource[]
16+
$levelQueues?: SlotsLevelQueues
1517
}
1618

1719
export type StaticSlots = Record<string, Slot>
@@ -23,7 +25,10 @@ export type DynamicSlotSource = StaticSlots | DynamicSlotFn
2325

2426
export 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

9061
const dynamicSlotsPropsProxyHandlers: ProxyHandler<RawProps> = {
@@ -151,3 +122,90 @@ export function createSlot(
151122

152123
// TODO
153124
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

Comments
 (0)