Skip to content

Commit cb2a17c

Browse files
authored
fix(runtime-vapor): preserve slot owner to ensure correct scopeId inheritance for nested components within v-for loops with slots. (#14353)
1 parent cf0730e commit cb2a17c

2 files changed

Lines changed: 79 additions & 2 deletions

File tree

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

Lines changed: 70 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
1-
import { createApp, h } from '@vue/runtime-dom'
1+
import { createApp, h, nextTick, ref } from '@vue/runtime-dom'
22
import {
33
createComponent,
44
createDynamicComponent,
5+
createFor,
56
createSlot,
67
defineVaporComponent,
78
setInsertionState,
@@ -374,6 +375,74 @@ describe('scopeId', () => {
374375
`</div>`,
375376
)
376377
})
378+
379+
test('nested components in vFor with slots', async () => {
380+
const Parent = defineVaporComponent({
381+
setup() {
382+
const n1 = template('<div>', true)() as any
383+
setInsertionState(n1, null, 0, true)
384+
createSlot('default', null)
385+
return n1
386+
},
387+
})
388+
389+
const Child = defineVaporComponent({
390+
setup() {
391+
const n1 = template('<div>', true)() as any
392+
setInsertionState(n1, null, 0, true)
393+
createSlot('default', null)
394+
return n1
395+
},
396+
})
397+
398+
const count = ref(0)
399+
const { html } = define({
400+
__scopeId: 'app',
401+
setup() {
402+
const n4 = createComponent(
403+
Parent,
404+
null,
405+
{
406+
default: withVaporCtx(() => {
407+
const n0 = createFor(
408+
() => count.value,
409+
_for_item0 => {
410+
const n3 = createComponent(
411+
Child,
412+
{ class: () => 'test' },
413+
{
414+
default: () => {
415+
const n2 = template('<div> red ')()
416+
return n2
417+
},
418+
},
419+
)
420+
return n3
421+
},
422+
item => item,
423+
2,
424+
)
425+
return n0
426+
}),
427+
},
428+
true,
429+
)
430+
return n4
431+
},
432+
}).render()
433+
434+
expect(html()).toBe(`<div app=""><!--for--><!--slot--></div>`)
435+
436+
count.value++
437+
await nextTick()
438+
expect(html()).toBe(
439+
`<div app="">` +
440+
`<div class="test" app="">` + // should have app scopeId
441+
`<div> red </div><!--slot-->` +
442+
`</div><!--for-->` +
443+
`<!--slot--></div>`,
444+
)
445+
})
377446
})
378447

379448
describe('vdom interop', () => {

packages/runtime-vapor/src/apiCreateFor.ts

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,11 @@ import {
2222
} from './block'
2323
import { warn } from '@vue/runtime-dom'
2424
import { currentInstance, isVaporComponent } from './component'
25-
import type { DynamicSlot } from './componentSlots'
25+
import {
26+
type DynamicSlot,
27+
currentSlotOwner,
28+
setCurrentSlotOwner,
29+
} from './componentSlots'
2630
import { renderEffect } from './renderEffect'
2731
import { VaporVForFlags } from '../../shared/src/vaporFlags'
2832
import {
@@ -123,6 +127,8 @@ export const createFor = (
123127
cleanup: () => void
124128
}[] = []
125129

130+
const scopeOwner = currentSlotOwner
131+
126132
if (__DEV__ && !instance) {
127133
warn('createFor() can only be used inside setup()')
128134
}
@@ -167,6 +173,7 @@ export const createFor = (
167173
}
168174
}
169175
} else {
176+
const prevOwner = setCurrentSlotOwner(scopeOwner)
170177
parent = parent || parentAnchor!.parentNode
171178
if (!oldLength) {
172179
// remove fallback nodes
@@ -394,6 +401,7 @@ export const createFor = (
394401
}
395402
}
396403
}
404+
setCurrentSlotOwner(prevOwner)
397405
}
398406

399407
if (!isFallback) {

0 commit comments

Comments
 (0)