201060717d
fix #3548, partial fix for #3569
119 lines
3.5 KiB
TypeScript
119 lines
3.5 KiB
TypeScript
import { ComponentInternalInstance } from './component'
|
|
import { devtoolsComponentUpdated } from './devtools'
|
|
import { setBlockTracking } from './vnode'
|
|
|
|
/**
|
|
* mark the current rendering instance for asset resolution (e.g.
|
|
* resolveComponent, resolveDirective) during render
|
|
*/
|
|
export let currentRenderingInstance: ComponentInternalInstance | null = null
|
|
export let currentScopeId: string | null = null
|
|
|
|
/**
|
|
* Note: rendering calls maybe nested. The function returns the parent rendering
|
|
* instance if present, which should be restored after the render is done:
|
|
*
|
|
* ```js
|
|
* const prev = setCurrentRenderingInstance(i)
|
|
* // ...render
|
|
* setCurrentRenderingInstance(prev)
|
|
* ```
|
|
*/
|
|
export function setCurrentRenderingInstance(
|
|
instance: ComponentInternalInstance | null
|
|
): ComponentInternalInstance | null {
|
|
const prev = currentRenderingInstance
|
|
currentRenderingInstance = instance
|
|
currentScopeId = (instance && instance.type.__scopeId) || null
|
|
// v2 pre-compiled components uses _scopeId instead of __scopeId
|
|
if (__COMPAT__ && !currentScopeId) {
|
|
currentScopeId = (instance && (instance.type as any)._scopeId) || null
|
|
}
|
|
return prev
|
|
}
|
|
|
|
/**
|
|
* Set scope id when creating hoisted vnodes.
|
|
* @private compiler helper
|
|
*/
|
|
export function pushScopeId(id: string | null) {
|
|
currentScopeId = id
|
|
}
|
|
|
|
/**
|
|
* Technically we no longer need this after 3.0.8 but we need to keep the same
|
|
* API for backwards compat w/ code generated by compilers.
|
|
* @private
|
|
*/
|
|
export function popScopeId() {
|
|
currentScopeId = null
|
|
}
|
|
|
|
/**
|
|
* Only for backwards compat
|
|
* @private
|
|
*/
|
|
export const withScopeId = (_id: string) => withCtx
|
|
|
|
export type ContextualRenderFn = {
|
|
(...args: any[]): any
|
|
_n: boolean /* already normalized */
|
|
_c: boolean /* compiled */
|
|
_d: boolean /* disableTracking */
|
|
_ns: boolean /* nonScoped */
|
|
}
|
|
|
|
/**
|
|
* Wrap a slot function to memoize current rendering instance
|
|
* @private compiler helper
|
|
*/
|
|
export function withCtx(
|
|
fn: Function,
|
|
ctx: ComponentInternalInstance | null = currentRenderingInstance,
|
|
isNonScopedSlot?: boolean // __COMPAT__ only
|
|
) {
|
|
if (!ctx) return fn
|
|
|
|
// already normalized
|
|
if ((fn as ContextualRenderFn)._n) {
|
|
return fn
|
|
}
|
|
|
|
const renderFnWithContext: ContextualRenderFn = (...args: any[]) => {
|
|
// If a user calls a compiled slot inside a template expression (#1745), it
|
|
// can mess up block tracking, so by default we disable block tracking and
|
|
// force bail out when invoking a compiled slot (indicated by the ._d flag).
|
|
// This isn't necessary if rendering a compiled `<slot>`, so we flip the
|
|
// ._d flag off when invoking the wrapped fn inside `renderSlot`.
|
|
if (renderFnWithContext._d) {
|
|
setBlockTracking(-1)
|
|
}
|
|
const prevInstance = setCurrentRenderingInstance(ctx)
|
|
const res = fn(...args)
|
|
setCurrentRenderingInstance(prevInstance)
|
|
if (renderFnWithContext._d) {
|
|
setBlockTracking(1)
|
|
}
|
|
|
|
if (__DEV__ || __FEATURE_PROD_DEVTOOLS__) {
|
|
devtoolsComponentUpdated(ctx)
|
|
}
|
|
|
|
return res
|
|
}
|
|
|
|
// mark normalized to avoid duplicated wrapping
|
|
renderFnWithContext._n = true
|
|
// mark this as compiled by default
|
|
// this is used in vnode.ts -> normalizeChildren() to set the slot
|
|
// rendering flag.
|
|
renderFnWithContext._c = true
|
|
// disable block tracking by default
|
|
renderFnWithContext._d = true
|
|
// compat build only flag to distinguish scoped slots from non-scoped ones
|
|
if (__COMPAT__ && isNonScopedSlot) {
|
|
renderFnWithContext._ns = true
|
|
}
|
|
return renderFnWithContext
|
|
}
|