Skip to content

Commit a779531

Browse files
authored
fix(runtime-vapor): preserve slot owner rendering context in resolveDynamicComponent (#14475)
close #14474
1 parent e0003aa commit a779531

File tree

3 files changed

+72
-3
lines changed

3 files changed

+72
-3
lines changed

packages/runtime-core/src/index.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -688,3 +688,7 @@ export {
688688
* @internal
689689
*/
690690
export { knownTemplateRefs, isTemplateRefKey } from './helpers/useTemplateRef'
691+
/**
692+
* @internal
693+
*/
694+
export { setCurrentRenderingInstance } from './componentRenderContext'

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

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,17 @@
11
import { ref, shallowRef } from '@vue/reactivity'
22
import { nextTick, resolveDynamicComponent } from '@vue/runtime-dom'
33
import {
4+
createComponent,
45
createComponentWithFallback,
56
createDynamicComponent,
7+
createSlot,
68
defineVaporComponent,
9+
insert,
710
renderEffect,
811
setHtml,
912
setInsertionState,
1013
template,
14+
withVaporCtx,
1115
} from '../src'
1216
import { makeRender } from './_utils'
1317

@@ -180,6 +184,49 @@ describe('api: createDynamicComponent', () => {
180184
)
181185
})
182186

187+
test('resolves slot owner local components after dynamic updates', async () => {
188+
const current = shallowRef('Foo')
189+
const Foo = defineVaporComponent({
190+
setup() {
191+
return template('<span>foo</span>')()
192+
},
193+
})
194+
const Child = defineVaporComponent({
195+
setup() {
196+
const n0 = template('<section></section>')()
197+
insert(createSlot('default'), n0 as ParentNode)
198+
return n0
199+
},
200+
})
201+
202+
const { html } = define({
203+
components: { Foo },
204+
setup() {
205+
return createComponent(Child, null, {
206+
default: withVaporCtx(() =>
207+
createDynamicComponent(() => current.value),
208+
),
209+
})
210+
},
211+
}).render()
212+
213+
expect(html()).toBe(
214+
'<section><span>foo</span><!--dynamic-component--><!--slot--></section>',
215+
)
216+
217+
current.value = 'div'
218+
await nextTick()
219+
expect(html()).toBe(
220+
'<section><div></div><!--dynamic-component--><!--slot--></section>',
221+
)
222+
223+
current.value = 'Foo'
224+
await nextTick()
225+
expect(html()).toBe(
226+
'<section><span>foo</span><!--dynamic-component--><!--slot--></section>',
227+
)
228+
})
229+
183230
test('accept blocks', async () => {
184231
const { html } = define({
185232
setup() {

packages/runtime-vapor/src/apiCreateDynamicComponent.ts

Lines changed: 21 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,20 @@
11
import {
2+
type ComponentInternalInstance,
23
currentInstance,
34
isKeepAlive,
45
isVNode,
56
resolveDynamicComponent,
7+
setCurrentRenderingInstance,
68
} from '@vue/runtime-dom'
79
import { insert, isBlock } from './block'
8-
import { createComponentWithFallback, emptyContext } from './component'
10+
import {
11+
type VaporComponentInstance,
12+
createComponentWithFallback,
13+
emptyContext,
14+
} from './component'
915
import { renderEffect } from './renderEffect'
1016
import type { RawProps } from './componentProps'
11-
import type { RawSlots } from './componentSlots'
17+
import { type RawSlots, getScopeOwner } from './componentSlots'
1218
import {
1319
insertionAnchor,
1420
insertionParent,
@@ -37,6 +43,7 @@ export function createDynamicComponent(
3743
? new DynamicFragment('dynamic-component')
3844
: new DynamicFragment()
3945

46+
const scopeOwner = getScopeOwner()
4047
const renderFn = () => {
4148
const value = getter()
4249
const appContext =
@@ -65,7 +72,7 @@ export function createDynamicComponent(
6572
}
6673

6774
return createComponentWithFallback(
68-
resolveDynamicComponent(value) as any,
75+
withScopeOwner(scopeOwner, () => resolveDynamicComponent(value)),
6976
rawProps,
7077
rawSlots,
7178
isSingleRoot,
@@ -87,3 +94,14 @@ export function createDynamicComponent(
8794
}
8895
return frag
8996
}
97+
98+
function withScopeOwner(owner: VaporComponentInstance | null, fn: () => any) {
99+
const prev = setCurrentRenderingInstance(
100+
owner as ComponentInternalInstance | null,
101+
)
102+
try {
103+
return fn()
104+
} finally {
105+
setCurrentRenderingInstance(prev)
106+
}
107+
}

0 commit comments

Comments
 (0)