Skip to content

Commit 7e0d813

Browse files
committed
refactor(runtime-vapor): use dedicated currentSlotOwner instead of changing currentInstance in withVaporCtx
1 parent 50602ec commit 7e0d813

File tree

5 files changed

+68
-58
lines changed

5 files changed

+68
-58
lines changed

packages/runtime-vapor/src/component.ts

Lines changed: 34 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -77,10 +77,10 @@ import {
7777
type RawSlots,
7878
type StaticSlots,
7979
type VaporSlot,
80+
currentSlotOwner,
8081
dynamicSlotsProxyHandlers,
81-
getParentInstance,
8282
getSlot,
83-
setCurrentSlotConsumer,
83+
setCurrentSlotOwner,
8484
} from './componentSlots'
8585
import { hmrReload, hmrRerender } from './hmr'
8686
import {
@@ -202,24 +202,22 @@ export function createComponent(
202202
resetInsertionState()
203203
}
204204

205-
const parentInstance = getParentInstance()
206-
207205
let prevSuspense: SuspenseBoundary | null = null
208-
if (__FEATURE_SUSPENSE__ && parentInstance && parentInstance.suspense) {
209-
prevSuspense = setParentSuspense(parentInstance.suspense)
206+
if (__FEATURE_SUSPENSE__ && currentInstance && currentInstance.suspense) {
207+
prevSuspense = setParentSuspense(currentInstance.suspense)
210208
}
211209

212210
if (
213211
(isSingleRoot ||
214212
// transition has attrs fallthrough
215-
(parentInstance && isVaporTransition(parentInstance!.type))) &&
213+
(currentInstance && isVaporTransition(currentInstance!.type))) &&
216214
component.inheritAttrs !== false &&
217-
isVaporComponent(parentInstance) &&
218-
parentInstance.hasFallthrough
215+
isVaporComponent(currentInstance) &&
216+
currentInstance.hasFallthrough
219217
) {
220218
// check if we are the single root of the parent
221219
// if yes, inject parent attrs as dynamic props source
222-
const attrs = parentInstance.attrs
220+
const attrs = currentInstance.attrs
223221
if (rawProps && rawProps !== EMPTY_OBJ) {
224222
;((rawProps as RawProps).$ || ((rawProps as RawProps).$ = [])).push(
225223
() => attrs,
@@ -230,8 +228,12 @@ export function createComponent(
230228
}
231229

232230
// keep-alive
233-
if (parentInstance && parentInstance.vapor && isKeepAlive(parentInstance)) {
234-
const cached = (parentInstance as KeepAliveInstance).getCachedComponent(
231+
if (
232+
currentInstance &&
233+
currentInstance.vapor &&
234+
isKeepAlive(currentInstance)
235+
) {
236+
const cached = (currentInstance as KeepAliveInstance).getCachedComponent(
235237
component,
236238
)
237239
// @ts-expect-error
@@ -240,14 +242,12 @@ export function createComponent(
240242

241243
// vdom interop enabled and component is not an explicit vapor component
242244
if (appContext.vapor && !component.__vapor) {
243-
const prevSlotConsumer = setCurrentSlotConsumer(null)
244245
const frag = appContext.vapor.vdomMount(
245246
component as any,
246-
parentInstance as any,
247+
currentInstance as any,
247248
rawProps,
248249
rawSlots,
249250
)
250-
setCurrentSlotConsumer(prevSlotConsumer)
251251
if (!isHydrating) {
252252
if (_insertionParent) insert(frag, _insertionParent, _insertionAnchor)
253253
} else {
@@ -280,11 +280,10 @@ export function createComponent(
280280
rawSlots as RawSlots,
281281
appContext,
282282
once,
283-
parentInstance,
284283
)
285284

286-
// set currentSlotConsumer to null to avoid affecting the child components
287-
const prevSlotConsumer = setCurrentSlotConsumer(null)
285+
// reset currentSlotOwner to null to avoid affecting the child components
286+
const prevSlotOwner = setCurrentSlotOwner(null)
288287

289288
// HMR
290289
if (__DEV__) {
@@ -347,12 +346,12 @@ export function createComponent(
347346
endMeasure(instance, 'init')
348347
}
349348

350-
if (__FEATURE_SUSPENSE__ && parentInstance && parentInstance.suspense) {
349+
if (__FEATURE_SUSPENSE__ && currentInstance && currentInstance.suspense) {
351350
setParentSuspense(prevSuspense)
352351
}
353352

354-
// restore currentSlotConsumer to previous value after setupFn is called
355-
setCurrentSlotConsumer(prevSlotConsumer)
353+
// restore currentSlotOwner to previous value after setupFn is called
354+
setCurrentSlotOwner(prevSlotOwner)
356355
onScopeDispose(() => unmountComponent(instance), true)
357356

358357
if (_insertionParent || isHydrating) {
@@ -594,19 +593,19 @@ export class VaporComponentInstance implements GenericComponentInstance {
594593
rawSlots?: RawSlots | null,
595594
appContext?: GenericAppContext,
596595
once?: boolean,
597-
parent: GenericComponentInstance | null = currentInstance,
598596
) {
599597
this.vapor = true
600598
this.uid = nextUid()
601599
this.type = comp
602-
this.parent = parent
603-
this.root = parent ? parent.root : this
600+
this.parent = currentInstance
604601

605-
if (parent) {
606-
this.appContext = parent.appContext
607-
this.provides = parent.provides
608-
this.ids = parent.ids
602+
if (currentInstance) {
603+
this.root = currentInstance.root
604+
this.appContext = currentInstance.appContext
605+
this.provides = currentInstance.provides
606+
this.ids = currentInstance.ids
609607
} else {
608+
this.root = this
610609
this.appContext = appContext || emptyContext
611610
this.provides = Object.create(this.appContext.provides)
612611
this.ids = ['', 0, 0]
@@ -655,7 +654,10 @@ export class VaporComponentInstance implements GenericComponentInstance {
655654
: rawSlots
656655
: EMPTY_OBJ
657656

658-
this.scopeId = currentInstance && currentInstance.type.__scopeId
657+
// Use currentSlotOwner for scopeId inheritance when inside a slot
658+
// This ensures components created in slots inherit the slot owner's scopeId
659+
const scopeOwner = currentSlotOwner || currentInstance
660+
this.scopeId = scopeOwner && scopeOwner.type.__scopeId
659661

660662
// apply custom element special handling
661663
if (comp.ce) {
@@ -745,7 +747,9 @@ export function createPlainElement(
745747
;(el as any).$root = isSingleRoot
746748

747749
if (!isHydrating) {
748-
const scopeId = currentInstance!.type.__scopeId
750+
// Use currentSlotOwner for scopeId when inside a slot
751+
const scopeOwner = currentSlotOwner || currentInstance
752+
const scopeId = scopeOwner!.type.__scopeId
749753
if (scopeId) setScopeId(el, [scopeId])
750754
}
751755

packages/runtime-vapor/src/componentSlots.ts

Lines changed: 26 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@ import {
66
currentInstance,
77
isAsyncWrapper,
88
isRef,
9-
setCurrentInstance,
109
} from '@vue/runtime-dom'
1110
import type { LooseRawProps, VaporComponentInstance } from './component'
1211
import { renderEffect } from './renderEffect'
@@ -124,42 +123,47 @@ export function getSlot(
124123
}
125124
}
126125

127-
export let currentSlotConsumer: GenericComponentInstance | null = null
126+
/**
127+
* Tracks the slot owner (the component that defines the slot content).
128+
* This is used for:
129+
* 1. Getting the correct rawSlots in forwarded slots (via createSlot)
130+
* 2. Inheriting the slot owner's scopeId
131+
*/
132+
export let currentSlotOwner: VaporComponentInstance | null = null
128133

129-
export function setCurrentSlotConsumer(
130-
consumer: GenericComponentInstance | null,
131-
): GenericComponentInstance | null {
134+
export function setCurrentSlotOwner(
135+
owner: VaporComponentInstance | null,
136+
): VaporComponentInstance | null {
132137
try {
133-
return currentSlotConsumer
138+
return currentSlotOwner
134139
} finally {
135-
currentSlotConsumer = consumer
140+
currentSlotOwner = owner
136141
}
137142
}
138143

139144
/**
140-
* use currentSlotConsumer as parent, the currentSlotConsumer will be reset to null
141-
* before setupFn call to avoid affecting children and restore to previous value
142-
* after setupFn is called
145+
* Get the effective slot instance for accessing rawSlots and scopeId.
146+
* Prefers currentSlotOwner (if inside a slot), falls back to currentInstance.
143147
*/
144-
export function getParentInstance(): GenericComponentInstance | null {
145-
return currentSlotConsumer || currentInstance
148+
export function getSlotInstance(): VaporComponentInstance {
149+
return (currentSlotOwner || currentInstance) as VaporComponentInstance
146150
}
147151

148152
/**
149-
* Wrap a slot function to memoize currentInstance
150-
* 1. ensure correct currentInstance in forwarded slots
151-
* 2. elements created in the slot inherit the slot owner's scopeId
153+
* Wrap a slot function to track the slot owner.
154+
*
155+
* This ensures:
156+
* 1. createSlot gets rawSlots from the correct component (slot owner)
157+
* 2. Elements inherit the slot owner's scopeId
152158
*/
153159
export function withVaporCtx(fn: Function): BlockFn {
154-
const owner = currentInstance
160+
const owner = currentInstance as VaporComponentInstance
155161
return (...args: any[]) => {
156-
const prev = setCurrentInstance(owner)
157-
const prevConsumer = setCurrentSlotConsumer(prev[0])
162+
const prevOwner = setCurrentSlotOwner(owner)
158163
try {
159164
return fn(...args)
160165
} finally {
161-
setCurrentInstance(...prev)
162-
setCurrentSlotConsumer(prevConsumer)
166+
setCurrentSlotOwner(prevOwner)
163167
}
164168
}
165169
}
@@ -176,7 +180,8 @@ export function createSlot(
176180
const _isLastInsertion = isLastInsertion
177181
if (!isHydrating) resetInsertionState()
178182

179-
const instance = currentInstance as VaporComponentInstance
183+
// Use slot owner if inside a slot (forwarded slots), otherwise use currentInstance
184+
const instance = getSlotInstance()
180185
const rawSlots = instance.rawSlots
181186
const slotProps = rawProps
182187
? new Proxy(rawProps, rawPropsProxyHandlers)

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

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import {
33
MismatchTypes,
44
type TeleportProps,
55
type TeleportTargetElement,
6+
currentInstance,
67
isMismatchAllowed,
78
isTeleportDeferred,
89
isTeleportDisabled,
@@ -31,7 +32,6 @@ import {
3132
setCurrentHydrationNode,
3233
} from '../dom/hydration'
3334
import { applyTransitionHooks } from './Transition'
34-
import { getParentInstance } from '../componentSlots'
3535

3636
export const VaporTeleportImpl = {
3737
name: 'VaporTeleport',
@@ -62,7 +62,7 @@ export class TeleportFragment extends VaporFragment {
6262
super([])
6363
this.rawProps = props
6464
this.rawSlots = slots
65-
this.parentComponent = getParentInstance()
65+
this.parentComponent = currentInstance
6666
this.anchor = isHydrating
6767
? undefined
6868
: __DEV__

packages/runtime-vapor/src/fragment.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ import {
1414
type GenericComponentInstance,
1515
type TransitionHooks,
1616
type VNode,
17+
currentInstance,
1718
queuePostFlushCb,
1819
setCurrentInstance,
1920
warnExtraneousAttributes,
@@ -31,7 +32,6 @@ import {
3132
locateFragmentEndAnchor,
3233
locateHydrationNode,
3334
} from './dom/hydration'
34-
import { getParentInstance } from './componentSlots'
3535
import { isArray } from '@vue/shared'
3636
import { renderEffect } from './renderEffect'
3737

@@ -99,7 +99,7 @@ export class DynamicFragment extends VaporFragment {
9999

100100
constructor(anchorLabel?: string) {
101101
super([])
102-
this.parentComponent = getParentInstance()
102+
this.parentComponent = currentInstance
103103
if (isHydrating) {
104104
this.anchorLabel = anchorLabel
105105
locateHydrationNode()

packages/runtime-vapor/src/vdomInterop.ts

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,7 @@ import {
6060
} from '@vue/shared'
6161
import { type RawProps, rawPropsProxyHandlers } from './componentProps'
6262
import type { RawSlots, VaporSlot } from './componentSlots'
63-
import { currentSlotScopeIds } from './componentSlots'
63+
import { currentSlotScopeIds, getSlotInstance } from './componentSlots'
6464
import { renderEffect } from './renderEffect'
6565
import { _next, createTextNode } from './dom/node'
6666
import { optimizePropertyLookup } from './dom/prop'
@@ -304,7 +304,6 @@ function createVDOMComponent(
304304
rawSlots as RawSlots,
305305
parentComponent ? parentComponent.appContext : undefined,
306306
undefined,
307-
parentComponent,
308307
)
309308

310309
// overwrite how the vdom instance handles props
@@ -351,7 +350,9 @@ function createVDOMComponent(
351350
frag.nodes = vnode.el as any
352351
}
353352

354-
vnode.scopeId = (currentInstance && currentInstance.type.__scopeId) || null
353+
// Use currentSlotOwner for scopeId when inside a slot
354+
const scopeOwner = getSlotInstance()
355+
vnode.scopeId = (scopeOwner && scopeOwner.type.__scopeId) || null
355356
vnode.slotScopeIds = currentSlotScopeIds
356357

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

0 commit comments

Comments
 (0)