fix(runtime-core): should not track dynamic children when the user calls a compiled slot inside template expression (#3554)

fix #3548, partial fix for #3569
This commit is contained in:
HcySunYang
2021-05-26 01:33:41 +08:00
committed by GitHub
parent 1526f94edf
commit 201060717d
7 changed files with 175 additions and 34 deletions

View File

@@ -1,7 +1,6 @@
import { ComponentInternalInstance } from './component'
import { devtoolsComponentUpdated } from './devtools'
import { isRenderingCompiledSlot } from './helpers/renderSlot'
import { closeBlock, openBlock } from './vnode'
import { setBlockTracking } from './vnode'
/**
* mark the current rendering instance for asset resolution (e.g.
@@ -56,6 +55,14 @@ export function popScopeId() {
*/
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
@@ -66,18 +73,26 @@ export function withCtx(
isNonScopedSlot?: boolean // __COMPAT__ only
) {
if (!ctx) return fn
const renderFnWithContext = (...args: any[]) => {
// 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 need to push a null block to
// avoid that. This isn't necessary if rendering a compiled `<slot>`.
if (!isRenderingCompiledSlot) {
openBlock(true /* null block that disables tracking */)
// 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 (!isRenderingCompiledSlot) {
closeBlock()
if (renderFnWithContext._d) {
setBlockTracking(1)
}
if (__DEV__ || __FEATURE_PROD_DEVTOOLS__) {
@@ -86,13 +101,18 @@ export function withCtx(
return res
}
// mark this as a compiled slot function.
// 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.
// also used to cache the normalized results to avoid repeated normalization
renderFnWithContext._c = renderFnWithContext
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._nonScoped = true
renderFnWithContext._ns = true
}
return renderFnWithContext
}