Skip to content

Commit 8a58dce

Browse files
committed
feat(runtime-core): set context for manual slot functions as well
1 parent ecd7ce6 commit 8a58dce

File tree

5 files changed

+37
-19
lines changed

5 files changed

+37
-19
lines changed

packages/runtime-core/src/component.ts

+3
Original file line numberDiff line numberDiff line change
@@ -494,6 +494,9 @@ const SetupProxyHandlers: { [key: string]: ProxyHandler<any> } = {}
494494
if (__DEV__) {
495495
markAttrsAccessed()
496496
}
497+
// if the user pass the slots proxy to h(), normalizeChildren should not
498+
// attempt to attach ctx to the object
499+
if (key === '_') return 1
497500
return instance[type][key]
498501
},
499502
has: (instance, key) => key === SetupProxySymbol || key in instance[type],

packages/runtime-core/src/componentSlots.ts

+22-14
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import {
88
import { isArray, isFunction, EMPTY_OBJ, ShapeFlags } from '@vue/shared'
99
import { warn } from './warning'
1010
import { isKeepAlive } from './components/KeepAlive'
11+
import { withCtx } from './helpers/withRenderContext'
1112

1213
export type Slot = (...args: any[]) => VNode[]
1314

@@ -21,6 +22,9 @@ export type RawSlots = {
2122
[name: string]: unknown
2223
// manual render fn hint to skip forced children updates
2324
$stable?: boolean
25+
// internal, for tracking slot owner instance. This is attached during
26+
// normalizeChildren when the component vnode is created.
27+
_ctx?: ComponentInternalInstance | null
2428
// internal, indicates compiler generated slots = can skip normalization
2529
_?: 1
2630
}
@@ -30,18 +34,21 @@ const normalizeSlotValue = (value: unknown): VNode[] =>
3034
? value.map(normalizeVNode)
3135
: [normalizeVNode(value as VNodeChild)]
3236

33-
const normalizeSlot = (key: string, rawSlot: Function): Slot => (
34-
props: any
35-
) => {
36-
if (__DEV__ && currentInstance != null) {
37-
warn(
38-
`Slot "${key}" invoked outside of the render function: ` +
39-
`this will not track dependencies used in the slot. ` +
40-
`Invoke the slot function inside the render function instead.`
41-
)
42-
}
43-
return normalizeSlotValue(rawSlot(props))
44-
}
37+
const normalizeSlot = (
38+
key: string,
39+
rawSlot: Function,
40+
ctx: ComponentInternalInstance | null | undefined
41+
): Slot =>
42+
withCtx((props: any) => {
43+
if (__DEV__ && currentInstance != null) {
44+
warn(
45+
`Slot "${key}" invoked outside of the render function: ` +
46+
`this will not track dependencies used in the slot. ` +
47+
`Invoke the slot function inside the render function instead.`
48+
)
49+
}
50+
return normalizeSlotValue(rawSlot(props))
51+
}, ctx)
4552

4653
export function resolveSlots(
4754
instance: ComponentInternalInstance,
@@ -55,11 +62,12 @@ export function resolveSlots(
5562
slots = children as Slots
5663
} else {
5764
slots = {}
65+
const ctx = rawSlots._ctx
5866
for (const key in rawSlots) {
59-
if (key === '$stable') continue
67+
if (key === '$stable' || key === '_ctx') continue
6068
const value = rawSlots[key]
6169
if (isFunction(value)) {
62-
slots[key] = normalizeSlot(key, value)
70+
slots[key] = normalizeSlot(key, value, ctx)
6371
} else if (value != null) {
6472
if (__DEV__) {
6573
warn(

packages/runtime-core/src/helpers/scopeId.ts

+2-3
Original file line numberDiff line numberDiff line change
@@ -24,13 +24,12 @@ export function popScopeId() {
2424
export function withScopeId(id: string): <T extends Function>(fn: T) => T {
2525
if (__BUNDLER__) {
2626
return ((fn: Function, ctx?: ComponentInternalInstance) => {
27-
function renderWithId(this: any) {
27+
return withCtx(function(this: any) {
2828
pushScopeId(id)
2929
const res = fn.apply(this, arguments)
3030
popScopeId()
3131
return res
32-
}
33-
return ctx ? withCtx(renderWithId, ctx) : renderWithId
32+
}, ctx)
3433
}) as any
3534
} else {
3635
return undefined as any

packages/runtime-core/src/helpers/withRenderContext.ts

+5-1
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,11 @@ import {
55
currentRenderingInstance
66
} from '../componentRenderUtils'
77

8-
export function withCtx(fn: Slot, ctx: ComponentInternalInstance) {
8+
export function withCtx(
9+
fn: Slot,
10+
ctx: ComponentInternalInstance | null | undefined
11+
) {
12+
if (!ctx) return fn
913
return function renderFnWithContext() {
1014
const owner = currentRenderingInstance
1115
setCurrentRenderingInstance(ctx)

packages/runtime-core/src/vnode.ts

+5-1
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ import { TransitionHooks } from './components/BaseTransition'
3030
import { warn } from './warning'
3131
import { currentScopeId } from './helpers/scopeId'
3232
import { PortalImpl, isPortal } from './components/Portal'
33+
import { currentRenderingInstance } from './componentRenderUtils'
3334

3435
export const Fragment = (Symbol(__DEV__ ? 'Fragment' : undefined) as any) as {
3536
__isFragment: true
@@ -387,8 +388,11 @@ export function normalizeChildren(vnode: VNode, children: unknown) {
387388
type = ShapeFlags.ARRAY_CHILDREN
388389
} else if (typeof children === 'object') {
389390
type = ShapeFlags.SLOTS_CHILDREN
391+
if (!(children as RawSlots)._) {
392+
;(children as RawSlots)._ctx = currentRenderingInstance
393+
}
390394
} else if (isFunction(children)) {
391-
children = { default: children }
395+
children = { default: children, _ctx: currentRenderingInstance }
392396
type = ShapeFlags.SLOTS_CHILDREN
393397
} else {
394398
children = String(children)

0 commit comments

Comments
 (0)