diff --git a/packages/runtime-core/src/componentRenderContext.ts b/packages/runtime-core/src/componentRenderContext.ts index 78297dc5..c74712a0 100644 --- a/packages/runtime-core/src/componentRenderContext.ts +++ b/packages/runtime-core/src/componentRenderContext.ts @@ -9,11 +9,23 @@ import { closeBlock, openBlock } from './vnode' 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 + return prev } /** @@ -40,8 +52,7 @@ export function withCtx( if (!isRenderingCompiledSlot) { openBlock(true /* null block that disables tracking */) } - const prevInstance = currentRenderingInstance - setCurrentRenderingInstance(ctx) + const prevInstance = setCurrentRenderingInstance(ctx) const res = fn(...args) setCurrentRenderingInstance(prevInstance) if (!isRenderingCompiledSlot) { diff --git a/packages/runtime-core/src/componentRenderUtils.ts b/packages/runtime-core/src/componentRenderUtils.ts index eee05f18..1274f474 100644 --- a/packages/runtime-core/src/componentRenderUtils.ts +++ b/packages/runtime-core/src/componentRenderUtils.ts @@ -53,7 +53,7 @@ export function renderComponentRoot( } = instance let result - setCurrentRenderingInstance(instance) + const prev = setCurrentRenderingInstance(instance) if (__DEV__) { accessedAttrs = false } @@ -207,7 +207,7 @@ export function renderComponentRoot( result = createVNode(Comment) } - setCurrentRenderingInstance(null) + setCurrentRenderingInstance(prev) return result } diff --git a/packages/server-renderer/__tests__/render.spec.ts b/packages/server-renderer/__tests__/render.spec.ts index bb7009f8..67bc2345 100644 --- a/packages/server-renderer/__tests__/render.spec.ts +++ b/packages/server-renderer/__tests__/render.spec.ts @@ -11,7 +11,9 @@ import { withCtx, KeepAlive, Transition, - watchEffect + watchEffect, + createVNode, + resolveDynamicComponent } from 'vue' import { escapeHtml } from '@vue/shared' import { renderToString } from '../src/renderToString' @@ -19,6 +21,7 @@ import { renderToStream as _renderToStream } from '../src/renderToStream' import { ssrRenderSlot, SSRSlot } from '../src/helpers/ssrRenderSlot' import { ssrRenderComponent } from '../src/helpers/ssrRenderComponent' import { Readable } from 'stream' +import { ssrRenderVNode } from '../src' const promisifyStream = (stream: Readable) => { return new Promise((resolve, reject) => { @@ -824,5 +827,34 @@ function testRender(type: string, render: typeof renderToString) { }) expect(await render(app)).toBe('') }) + + // #2863 + test('assets should be resolved correctly', async () => { + expect( + await render( + createApp({ + components: { + A: { + ssrRender(_ctx, _push) { + _push(`
A
`) + } + }, + B: { + render: () => h('div', 'B') + } + }, + ssrRender(_ctx, _push, _parent) { + const A: any = resolveComponent('A') + _push(ssrRenderComponent(A, null, null, _parent)) + ssrRenderVNode( + _push, + createVNode(resolveDynamicComponent('B'), null, null), + _parent + ) + } + }) + ) + ).toBe(`
A
B
`) + }) }) } diff --git a/packages/server-renderer/src/render.ts b/packages/server-renderer/src/render.ts index 77a268b5..4f20e52e 100644 --- a/packages/server-renderer/src/render.ts +++ b/packages/server-renderer/src/render.ts @@ -139,7 +139,7 @@ function renderComponentSubTree( } // set current rendering instance for asset resolution - setCurrentRenderingInstance(instance) + const prev = setCurrentRenderingInstance(instance) ssrRender( instance.proxy, push, @@ -151,7 +151,7 @@ function renderComponentSubTree( instance.data, instance.ctx ) - setCurrentRenderingInstance(null) + setCurrentRenderingInstance(prev) } else if (instance.render && instance.render !== NOOP) { renderVNode( push,