Skip to content

Commit

Permalink
feat(runtime-core): set context for manual slot functions as well
Browse files Browse the repository at this point in the history
  • Loading branch information
yyx990803 committed Mar 16, 2020
1 parent ecd7ce6 commit 8a58dce
Show file tree
Hide file tree
Showing 5 changed files with 37 additions and 19 deletions.
3 changes: 3 additions & 0 deletions packages/runtime-core/src/component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -494,6 +494,9 @@ const SetupProxyHandlers: { [key: string]: ProxyHandler<any> } = {}
if (__DEV__) {
markAttrsAccessed()
}
// if the user pass the slots proxy to h(), normalizeChildren should not
// attempt to attach ctx to the object
if (key === '_') return 1
return instance[type][key]
},
has: (instance, key) => key === SetupProxySymbol || key in instance[type],
Expand Down
36 changes: 22 additions & 14 deletions packages/runtime-core/src/componentSlots.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import {
import { isArray, isFunction, EMPTY_OBJ, ShapeFlags } from '@vue/shared'
import { warn } from './warning'
import { isKeepAlive } from './components/KeepAlive'
import { withCtx } from './helpers/withRenderContext'

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

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

const normalizeSlot = (key: string, rawSlot: Function): Slot => (
props: any
) => {
if (__DEV__ && currentInstance != null) {
warn(
`Slot "${key}" invoked outside of the render function: ` +
`this will not track dependencies used in the slot. ` +
`Invoke the slot function inside the render function instead.`
)
}
return normalizeSlotValue(rawSlot(props))
}
const normalizeSlot = (
key: string,
rawSlot: Function,
ctx: ComponentInternalInstance | null | undefined
): Slot =>
withCtx((props: any) => {
if (__DEV__ && currentInstance != null) {
warn(
`Slot "${key}" invoked outside of the render function: ` +
`this will not track dependencies used in the slot. ` +
`Invoke the slot function inside the render function instead.`
)
}
return normalizeSlotValue(rawSlot(props))
}, ctx)

export function resolveSlots(
instance: ComponentInternalInstance,
Expand All @@ -55,11 +62,12 @@ export function resolveSlots(
slots = children as Slots
} else {
slots = {}
const ctx = rawSlots._ctx
for (const key in rawSlots) {
if (key === '$stable') continue
if (key === '$stable' || key === '_ctx') continue
const value = rawSlots[key]
if (isFunction(value)) {
slots[key] = normalizeSlot(key, value)
slots[key] = normalizeSlot(key, value, ctx)
} else if (value != null) {
if (__DEV__) {
warn(
Expand Down
5 changes: 2 additions & 3 deletions packages/runtime-core/src/helpers/scopeId.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,13 +24,12 @@ export function popScopeId() {
export function withScopeId(id: string): <T extends Function>(fn: T) => T {
if (__BUNDLER__) {
return ((fn: Function, ctx?: ComponentInternalInstance) => {
function renderWithId(this: any) {
return withCtx(function(this: any) {
pushScopeId(id)
const res = fn.apply(this, arguments)
popScopeId()
return res
}
return ctx ? withCtx(renderWithId, ctx) : renderWithId
}, ctx)
}) as any
} else {
return undefined as any
Expand Down
6 changes: 5 additions & 1 deletion packages/runtime-core/src/helpers/withRenderContext.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,11 @@ import {
currentRenderingInstance
} from '../componentRenderUtils'

export function withCtx(fn: Slot, ctx: ComponentInternalInstance) {
export function withCtx(
fn: Slot,
ctx: ComponentInternalInstance | null | undefined
) {
if (!ctx) return fn
return function renderFnWithContext() {
const owner = currentRenderingInstance
setCurrentRenderingInstance(ctx)
Expand Down
6 changes: 5 additions & 1 deletion packages/runtime-core/src/vnode.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ import { TransitionHooks } from './components/BaseTransition'
import { warn } from './warning'
import { currentScopeId } from './helpers/scopeId'
import { PortalImpl, isPortal } from './components/Portal'
import { currentRenderingInstance } from './componentRenderUtils'

export const Fragment = (Symbol(__DEV__ ? 'Fragment' : undefined) as any) as {
__isFragment: true
Expand Down Expand Up @@ -387,8 +388,11 @@ export function normalizeChildren(vnode: VNode, children: unknown) {
type = ShapeFlags.ARRAY_CHILDREN
} else if (typeof children === 'object') {
type = ShapeFlags.SLOTS_CHILDREN
if (!(children as RawSlots)._) {
;(children as RawSlots)._ctx = currentRenderingInstance
}
} else if (isFunction(children)) {
children = { default: children }
children = { default: children, _ctx: currentRenderingInstance }
type = ShapeFlags.SLOTS_CHILDREN
} else {
children = String(children)
Expand Down

0 comments on commit 8a58dce

Please sign in to comment.