Skip to content

Commit 6957448

Browse files
committed
feat: dynamic remove keys
1 parent cbd9a1b commit 6957448

File tree

2 files changed

+128
-24
lines changed

2 files changed

+128
-24
lines changed

packages/runtime-vapor/__tests__/componentSlots.spec.ts

+100-4
Original file line numberDiff line numberDiff line change
@@ -348,15 +348,69 @@ describe('component: slots', () => {
348348
expect(host.innerHTML).toBe('<div><h1>header</h1></div>')
349349
})
350350

351+
test('dynamic slot props', async () => {
352+
let props: any
353+
354+
const bindObj = ref<Record<string, any>>({ foo: 1, baz: 'qux' })
355+
const Comp = defineComponent(() =>
356+
createSlot('default', [() => bindObj.value]),
357+
)
358+
define(() =>
359+
createComponent(
360+
Comp,
361+
{},
362+
{ default: _props => ((props = _props), []) },
363+
),
364+
).render()
365+
366+
expect(props).toEqual({ foo: 1, baz: 'qux' })
367+
368+
bindObj.value.foo = 2
369+
await nextTick()
370+
expect(props).toEqual({ foo: 2, baz: 'qux' })
371+
372+
delete bindObj.value.baz
373+
await nextTick()
374+
expect(props).toEqual({ foo: 2 })
375+
})
376+
377+
test('dynamic slot props with static slot props', async () => {
378+
let props: any
379+
380+
const foo = ref(0)
381+
const bindObj = ref<Record<string, any>>({ foo: 100, baz: 'qux' })
382+
const Comp = defineComponent(() =>
383+
createSlot('default', [{ foo: () => foo.value }, () => bindObj.value]),
384+
)
385+
define(() =>
386+
createComponent(
387+
Comp,
388+
{},
389+
{ default: _props => ((props = _props), []) },
390+
),
391+
).render()
392+
393+
expect(props).toEqual({ foo: 100, baz: 'qux' })
394+
395+
foo.value = 2
396+
await nextTick()
397+
expect(props).toEqual({ foo: 100, baz: 'qux' })
398+
399+
delete bindObj.value.foo
400+
await nextTick()
401+
expect(props).toEqual({ foo: 2, baz: 'qux' })
402+
})
403+
351404
test('slot class binding should be merged', async () => {
352405
let props: any
353406

354-
const enable = ref(true)
407+
const className = ref('foo')
408+
const classObj = ref({ bar: true })
355409
const Comp = defineComponent(() =>
356410
createSlot('default', [
357-
{ class: () => 'foo' },
411+
{ class: () => className.value },
358412
() => ({ class: ['baz', 'qux'] }),
359-
{ class: () => ({ bar: enable.value }) },
413+
{ class: () => classObj.value },
360414
]),
361415
)
362416
define(() =>
@@ -368,9 +422,51 @@ describe('component: slots', () => {
368422
).render()
369423

370424
expect(props).toEqual({ class: 'foo baz qux bar' })
371-
enable.value = false
425+
426+
classObj.value.bar = false
372427
await nextTick()
373428
expect(props).toEqual({ class: 'foo baz qux' })
429+
430+
className.value = ''
431+
await nextTick()
432+
expect(props).toEqual({ class: 'baz qux' })
433+
})
434+
435+
test('slot style binding should be merged', async () => {
436+
let props: any
437+
438+
const style = ref<any>({ fontSize: '12px' })
439+
const Comp = defineComponent(() =>
440+
createSlot('default', [
441+
{ style: () => style.value },
442+
() => ({ style: { width: '100px', color: 'blue' } }),
443+
{ style: () => 'color: red' },
444+
]),
445+
)
446+
define(() =>
447+
createComponent(
448+
Comp,
449+
{},
450+
{ default: _props => ((props = _props), []) },
451+
),
452+
).render()
453+
454+
expect(props).toEqual({
455+
style: {
456+
fontSize: '12px',
457+
width: '100px',
458+
color: 'red',
459+
},
460+
})
461+
462+
style.value = null
463+
await nextTick()
464+
expect(props).toEqual({
465+
style: {
466+
width: '100px',
467+
color: 'red',
468+
},
469+
})
374470
})
375471

376472
test('dynamic slot should be render correctly with binds', async () => {

packages/runtime-vapor/src/componentSlots.ts

+28-20
Original file line numberDiff line numberDiff line change
@@ -159,53 +159,61 @@ export function createSlot(
159159

160160
return fragment
161161

162-
function withProps(fn: Slot | undefined): Slot | undefined {
162+
function withProps<T extends (p: any) => any>(fn?: T) {
163163
if (fn)
164-
return (binds?: NormalizedRawProps) =>
164+
return (binds?: NormalizedRawProps): ReturnType<T> =>
165165
fn(binds && normalizeSlotProps(binds))
166166
}
167167
}
168168

169169
function normalizeSlotProps(rawPropsList: NormalizedRawProps) {
170-
const ret = shallowReactive<Data>({})
171170
const { length } = rawPropsList
172-
const isNeedToMerge = length > 1
173-
const dataCache = isNeedToMerge ? shallowReactive<Data[]>([]) : undefined
171+
const mergings = length > 1 ? shallowReactive<Data[]>([]) : undefined
172+
const result = shallowReactive<Data>({})
174173

175174
for (let i = 0; i < length; i++) {
176175
const rawProps = rawPropsList[i]
177176
if (isFunction(rawProps)) {
177+
// dynamic props
178178
renderEffect(() => {
179179
const props = rawProps()
180-
if (isNeedToMerge) {
181-
dataCache![i] = props
180+
if (mergings) {
181+
mergings[i] = props
182182
} else {
183-
for (const key in props) {
184-
ret[key] = props[key]
185-
}
183+
setDynamicProps(props)
186184
}
187185
})
188186
} else {
189-
const itemRet = isNeedToMerge
190-
? (dataCache![i] = shallowReactive<Data>({}))
191-
: ret
187+
// static props
188+
const props = mergings
189+
? (mergings[i] = shallowReactive<Data>({}))
190+
: result
192191
for (const key in rawProps) {
193192
const valueSource = rawProps[key]
194193
renderEffect(() => {
195-
itemRet[key] = valueSource()
194+
props[key] = valueSource()
196195
})
197196
}
198197
}
199198
}
200199

201-
if (isNeedToMerge) {
200+
if (mergings) {
202201
renderEffect(() => {
203-
const props = mergeProps(...dataCache!)
204-
for (const key in props) {
205-
ret[key] = props[key]
206-
}
202+
setDynamicProps(mergeProps(...mergings))
207203
})
208204
}
209205

210-
return ret
206+
return result
207+
208+
function setDynamicProps(props: Data) {
209+
const otherExistingKeys = new Set(Object.keys(result))
210+
for (const key in props) {
211+
result[key] = props[key]
212+
otherExistingKeys.delete(key)
213+
}
214+
// delete other stale props
215+
for (const key of otherExistingKeys) {
216+
delete result[key]
217+
}
218+
}
211219
}

0 commit comments

Comments
 (0)