Skip to content

Commit 3b5f994

Browse files
committed
fix(runtime-vapor): track actual parent instance for slot children
1 parent abe8fc2 commit 3b5f994

File tree

7 files changed

+76
-37
lines changed

7 files changed

+76
-37
lines changed

packages/runtime-vapor/src/apiCreateDynamicComponent.ts

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
1-
import { currentInstance, resolveDynamicComponent } from '@vue/runtime-dom'
1+
import { resolveDynamicComponent } from '@vue/runtime-dom'
2+
import { getParentInstance } from './componentSlots'
23
import { insert } from './block'
34
import { createComponentWithFallback, emptyContext } from './component'
45
import { renderEffect } from './renderEffect'
@@ -32,8 +33,8 @@ export function createDynamicComponent(
3233

3334
const renderFn = () => {
3435
const value = getter()
35-
const appContext =
36-
(currentInstance && currentInstance.appContext) || emptyContext
36+
const parent = getParentInstance()
37+
const appContext = (parent && parent.appContext) || emptyContext
3738
frag.update(
3839
() =>
3940
createComponentWithFallback(

packages/runtime-vapor/src/block.ts

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -253,19 +253,23 @@ export function setScopeId(block: Block, scopeIds: string[]): void {
253253

254254
export function setComponentScopeId(instance: VaporComponentInstance): void {
255255
const parent = instance.parent
256-
if (!parent) return
256+
const scopeOwner = instance.scopeOwner || parent
257+
if (!scopeOwner && !parent) return
257258
// prevent setting scopeId on multi-root fragments
258259
if (isArray(instance.block) && instance.block.length > 1) return
259260

260261
const scopeIds: string[] = []
261262

262-
const scopeId = parent.type.__scopeId
263-
if (scopeId) {
264-
scopeIds.push(scopeId)
263+
if (scopeOwner) {
264+
const scopeId = scopeOwner.type.__scopeId
265+
if (scopeId) {
266+
scopeIds.push(scopeId)
267+
}
265268
}
266269

267270
// inherit scopeId from vdom parent
268271
if (
272+
parent &&
269273
parent.subTree &&
270274
(parent.subTree.component as any) === instance &&
271275
parent.vnode!.scopeId

packages/runtime-vapor/src/component.ts

Lines changed: 28 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,7 @@ import {
7070
type StaticSlots,
7171
type VaporSlot,
7272
dynamicSlotsProxyHandlers,
73+
getParentInstance,
7374
getSlot,
7475
} from './componentSlots'
7576
import { hmrReload, hmrRerender } from './hmr'
@@ -178,9 +179,7 @@ export function createComponent(
178179
rawSlots?: LooseRawSlots | null,
179180
isSingleRoot?: boolean,
180181
once?: boolean,
181-
appContext: GenericAppContext = (currentInstance &&
182-
currentInstance.appContext) ||
183-
emptyContext,
182+
appContext: GenericAppContext = emptyContext,
184183
): VaporComponentInstance {
185184
const _insertionParent = insertionParent
186185
const _insertionAnchor = insertionAnchor
@@ -191,15 +190,23 @@ export function createComponent(
191190
resetInsertionState()
192191
}
193192

193+
const parentInstance = getParentInstance()
194+
appContext =
195+
appContext !== emptyContext
196+
? appContext
197+
: parentInstance
198+
? parentInstance.appContext
199+
: emptyContext
200+
194201
if (
195202
isSingleRoot &&
196203
component.inheritAttrs !== false &&
197-
isVaporComponent(currentInstance) &&
198-
currentInstance.hasFallthrough
204+
isVaporComponent(parentInstance) &&
205+
parentInstance.hasFallthrough
199206
) {
200207
// check if we are the single root of the parent
201208
// if yes, inject parent attrs as dynamic props source
202-
const attrs = currentInstance.attrs
209+
const attrs = parentInstance.attrs
203210
if (rawProps) {
204211
;((rawProps as RawProps).$ || ((rawProps as RawProps).$ = [])).push(
205212
() => attrs,
@@ -210,12 +217,8 @@ export function createComponent(
210217
}
211218

212219
// keep-alive
213-
if (
214-
currentInstance &&
215-
currentInstance.vapor &&
216-
isKeepAlive(currentInstance)
217-
) {
218-
const cached = (currentInstance as KeepAliveInstance).getCachedComponent(
220+
if (parentInstance && parentInstance.vapor && isKeepAlive(parentInstance)) {
221+
const cached = (parentInstance as KeepAliveInstance).getCachedComponent(
219222
component,
220223
)
221224
// @ts-expect-error
@@ -262,6 +265,7 @@ export function createComponent(
262265
rawSlots as RawSlots,
263266
appContext,
264267
once,
268+
parentInstance,
265269
)
266270

267271
// HMR
@@ -525,6 +529,9 @@ export class VaporComponentInstance implements GenericComponentInstance {
525529
ec?: LifecycleHook // LifecycleHooks.ERROR_CAPTURED
526530
sp?: LifecycleHook<() => Promise<unknown>> // LifecycleHooks.SERVER_PREFETCH
527531

532+
// Render-context owner used for scopeId inheritance.
533+
scopeOwner?: GenericComponentInstance | null
534+
528535
// dev only
529536
setupState?: Record<string, any>
530537
devtoolsRawSetupState?: any
@@ -541,17 +548,19 @@ export class VaporComponentInstance implements GenericComponentInstance {
541548
rawSlots?: RawSlots | null,
542549
appContext?: GenericAppContext,
543550
once?: boolean,
551+
parent: GenericComponentInstance | null = currentInstance,
544552
) {
545553
this.vapor = true
546554
this.uid = nextUid()
547555
this.type = comp
548-
this.parent = currentInstance
549-
this.root = currentInstance ? currentInstance.root : this
550-
551-
if (currentInstance) {
552-
this.appContext = currentInstance.appContext
553-
this.provides = currentInstance.provides
554-
this.ids = currentInstance.ids
556+
this.parent = parent
557+
this.scopeOwner = currentInstance
558+
this.root = parent ? parent.root : this
559+
560+
if (parent) {
561+
this.appContext = parent.appContext
562+
this.provides = parent.provides
563+
this.ids = parent.ids
555564
} else {
556565
this.appContext = appContext || emptyContext
557566
this.provides = Object.create(this.appContext.provides)

packages/runtime-vapor/src/componentSlots.ts

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -122,6 +122,17 @@ export function getSlot(
122122
}
123123
}
124124

125+
// Tracks the instance that owned the render context when a slot was created.
126+
const slotParentStack: (GenericComponentInstance | null)[] = []
127+
128+
export function getParentInstance(): GenericComponentInstance | null {
129+
const parent =
130+
slotParentStack.length > 0
131+
? slotParentStack[slotParentStack.length - 1]
132+
: null
133+
return parent || currentInstance
134+
}
135+
125136
/**
126137
* Wraps a slot function to execute in the parent component's context.
127138
*
@@ -140,9 +151,11 @@ export function withVaporCtx(fn: Function): BlockFn {
140151
const instance = currentInstance as VaporComponentInstance
141152
return (...args: any[]) => {
142153
const prev = setCurrentInstance(instance)
154+
slotParentStack.push(prev[0])
143155
try {
144156
return fn(...args)
145157
} finally {
158+
slotParentStack.pop()
146159
setCurrentInstance(...prev)
147160
}
148161
}

packages/runtime-vapor/src/components/Teleport.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,14 +3,14 @@ import {
33
MismatchTypes,
44
type TeleportProps,
55
type TeleportTargetElement,
6-
currentInstance,
76
isMismatchAllowed,
87
isTeleportDeferred,
98
isTeleportDisabled,
109
queuePostFlushCb,
1110
resolveTeleportTarget,
1211
warn,
1312
} from '@vue/runtime-dom'
13+
import { getParentInstance } from '../componentSlots'
1414
import { type Block, type BlockFn, insert, remove } from '../block'
1515
import { createComment, createTextNode, querySelector } from '../dom/node'
1616
import {
@@ -56,13 +56,13 @@ export class TeleportFragment extends VaporFragment {
5656
placeholder?: Node
5757
mountContainer?: ParentNode | null
5858
mountAnchor?: Node | null
59-
parentComponent: GenericComponentInstance
59+
parentComponent: GenericComponentInstance | null
6060

6161
constructor(props: LooseRawProps, slots: LooseRawSlots) {
6262
super([])
6363
this.rawProps = props
6464
this.rawSlots = slots
65-
this.parentComponent = currentInstance as GenericComponentInstance
65+
this.parentComponent = getParentInstance()
6666
this.anchor = isHydrating
6767
? undefined
6868
: __DEV__

packages/runtime-vapor/src/dom/prop.ts

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,6 @@ import {
1515
import { on } from './event'
1616
import {
1717
MismatchTypes,
18-
currentInstance,
1918
getAttributeMismatch,
2019
isMapEqual,
2120
isMismatchAllowed,
@@ -37,6 +36,7 @@ import {
3736
isApplyingFallthroughProps,
3837
isVaporComponent,
3938
} from '../component'
39+
import { getParentInstance } from '../componentSlots'
4040
import { isHydrating, logMismatchError } from './hydration'
4141
import type { Block } from '../block'
4242
import type { VaporElement } from '../apiDefineVaporCustomElement'
@@ -50,9 +50,14 @@ type TargetElement = Element & {
5050
_value?: any
5151
}
5252

53-
const hasFallthroughKey = (key: string) =>
54-
(currentInstance as VaporComponentInstance).hasFallthrough &&
55-
key in currentInstance!.attrs
53+
const hasFallthroughKey = (key: string) => {
54+
const instance = getParentInstance()
55+
return !!(
56+
instance &&
57+
(instance as VaporComponentInstance).hasFallthrough &&
58+
key in instance.attrs
59+
)
60+
}
5661

5762
export function setProp(el: any, key: string, value: any): void {
5863
if (key in el) {

packages/runtime-vapor/src/vdomInterop.ts

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,7 @@ import {
5959
} from '@vue/shared'
6060
import { type RawProps, rawPropsProxyHandlers } from './componentProps'
6161
import type { RawSlots, VaporSlot } from './componentSlots'
62-
import { currentSlotScopeIds } from './componentSlots'
62+
import { currentSlotScopeIds, getParentInstance } from './componentSlots'
6363
import { renderEffect } from './renderEffect'
6464
import { _next, createTextNode } from './dom/node'
6565
import { optimizePropertyLookup } from './dom/prop'
@@ -276,7 +276,8 @@ function createVDOMComponent(
276276
rawProps?: LooseRawProps | null,
277277
rawSlots?: LooseRawSlots | null,
278278
): VaporFragment {
279-
const parentInstance = currentInstance as VaporComponentInstance
279+
const parentInstance = getParentInstance() as VaporComponentInstance | null
280+
const scopeOwner = currentInstance as VaporComponentInstance | null
280281
const frag = new VaporFragment([])
281282
const vnode = (frag.vnode = createVNode(
282283
component,
@@ -286,6 +287,9 @@ function createVDOMComponent(
286287
{ props: component.props },
287288
rawProps as RawProps,
288289
rawSlots as RawSlots,
290+
parentInstance ? parentInstance.appContext : undefined,
291+
undefined,
292+
parentInstance,
289293
)
290294

291295
// overwrite how the vdom instance handles props
@@ -312,7 +316,10 @@ function createVDOMComponent(
312316
// unset ref
313317
if (rawRef) vdomSetRef(rawRef, null, null, vnode, true)
314318
if (transition) setVNodeTransitionHooks(vnode, transition)
315-
if (vnode.shapeFlag & ShapeFlags.COMPONENT_SHOULD_KEEP_ALIVE) {
319+
if (
320+
parentInstance &&
321+
vnode.shapeFlag & ShapeFlags.COMPONENT_SHOULD_KEEP_ALIVE
322+
) {
316323
vdomDeactivate(
317324
vnode,
318325
findParentKeepAlive(parentInstance)!.getStorageContainer(),
@@ -332,7 +339,7 @@ function createVDOMComponent(
332339
frag.nodes = vnode.el as any
333340
}
334341

335-
vnode.scopeId = parentInstance && parentInstance.type.__scopeId!
342+
vnode.scopeId = scopeOwner && scopeOwner.type.__scopeId!
336343
vnode.slotScopeIds = currentSlotScopeIds
337344

338345
frag.insert = (parentNode, anchor, transition) => {

0 commit comments

Comments
 (0)