From 08a3d95e5213c717dde43a9b58d18dc37e6872d9 Mon Sep 17 00:00:00 2001 From: Evan You Date: Mon, 25 Nov 2019 10:04:40 -0500 Subject: [PATCH] fix: resolveDynamicComponent should use context instance --- .../transforms/transformElement.spec.ts | 3 +- .../src/transforms/transformElement.ts | 3 +- packages/runtime-core/src/componentProxy.ts | 3 ++ .../runtime-core/src/helpers/resolveAssets.ts | 35 +++++++++++++++---- 4 files changed, 35 insertions(+), 9 deletions(-) diff --git a/packages/compiler-core/__tests__/transforms/transformElement.spec.ts b/packages/compiler-core/__tests__/transforms/transformElement.spec.ts index 00f5274a..078ea9bc 100644 --- a/packages/compiler-core/__tests__/transforms/transformElement.spec.ts +++ b/packages/compiler-core/__tests__/transforms/transformElement.spec.ts @@ -788,7 +788,8 @@ describe('compiler: element transform', () => { { type: NodeTypes.SIMPLE_EXPRESSION, content: 'foo' - } + }, + '$' ] } ]) diff --git a/packages/compiler-core/src/transforms/transformElement.ts b/packages/compiler-core/src/transforms/transformElement.ts index e70588bb..e440bb66 100644 --- a/packages/compiler-core/src/transforms/transformElement.ts +++ b/packages/compiler-core/src/transforms/transformElement.ts @@ -86,7 +86,8 @@ export const transformElement: NodeTransform = (node, context) => { else if (isProp.exp) { dynamicComponent = createCallExpression( context.helper(RESOLVE_DYNAMIC_COMPONENT), - [isProp.exp] + // _ctx.$ exposes the owner instance of current render function + [isProp.exp, context.prefixIdentifiers ? `_ctx.$` : `$`] ) } } diff --git a/packages/runtime-core/src/componentProxy.ts b/packages/runtime-core/src/componentProxy.ts index ce3e36c9..53f5aeba 100644 --- a/packages/runtime-core/src/componentProxy.ts +++ b/packages/runtime-core/src/componentProxy.ts @@ -106,6 +106,9 @@ export const PublicInstanceProxyHandlers: ProxyHandler = { } // return the value from propsProxy for ref unwrapping and readonly return propsProxy![key] + } else if (key === '$') { + // reserved backdoor to access the internal instance + return target } else if (key === '$cache') { return target.renderCache || (target.renderCache = []) } else if (key === '$el') { diff --git a/packages/runtime-core/src/helpers/resolveAssets.ts b/packages/runtime-core/src/helpers/resolveAssets.ts index 91283f0f..423cd20b 100644 --- a/packages/runtime-core/src/helpers/resolveAssets.ts +++ b/packages/runtime-core/src/helpers/resolveAssets.ts @@ -1,5 +1,9 @@ import { currentRenderingInstance } from '../componentRenderUtils' -import { currentInstance, Component } from '../component' +import { + currentInstance, + Component, + ComponentInternalInstance +} from '../component' import { Directive } from '../directives' import { camelize, @@ -15,11 +19,16 @@ export function resolveComponent(name: string): Component | undefined { } export function resolveDynamicComponent( - component: unknown + component: unknown, + // Dynamic component resolution has to be called inline due to potential + // access to scope variables. When called inside slots it will be inside + // a different component's render cycle, so the owner instance must be passed + // in explicitly. + instance: ComponentInternalInstance ): Component | undefined { if (!component) return if (isString(component)) { - return resolveAsset('components', component) + return resolveAsset('components', component, instance) } else if (isFunction(component) || isObject(component)) { return component } @@ -30,12 +39,24 @@ export function resolveDirective(name: string): Directive | undefined { } // overload 1: components -function resolveAsset(type: 'components', name: string): Component | undefined +function resolveAsset( + type: 'components', + name: string, + instance?: ComponentInternalInstance +): Component | undefined // overload 2: directives -function resolveAsset(type: 'directives', name: string): Directive | undefined +function resolveAsset( + type: 'directives', + name: string, + instance?: ComponentInternalInstance +): Directive | undefined -function resolveAsset(type: 'components' | 'directives', name: string) { - const instance = currentRenderingInstance || currentInstance +function resolveAsset( + type: 'components' | 'directives', + name: string, + instance: ComponentInternalInstance | null = currentRenderingInstance || + currentInstance +) { if (instance) { let camelized const registry = instance[type]