feat(runtime-core): set context for manual slot functions as well

This commit is contained in:
Evan You 2020-03-16 12:20:52 -04:00
parent ecd7ce60d5
commit 8a58dce603
5 changed files with 37 additions and 19 deletions

View File

@ -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],

View File

@ -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[]
@ -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
}
@ -30,9 +34,12 @@ const normalizeSlotValue = (value: unknown): VNode[] =>
? value.map(normalizeVNode)
: [normalizeVNode(value as VNodeChild)]
const normalizeSlot = (key: string, rawSlot: Function): Slot => (
props: any
) => {
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: ` +
@ -41,7 +48,7 @@ const normalizeSlot = (key: string, rawSlot: Function): Slot => (
)
}
return normalizeSlotValue(rawSlot(props))
}
}, ctx)
export function resolveSlots(
instance: ComponentInternalInstance,
@ -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(

View File

@ -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

View File

@ -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)

View File

@ -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
@ -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)