diff --git a/packages/server-renderer/__tests__/ssrScopeId.spec.ts b/packages/server-renderer/__tests__/ssrScopeId.spec.ts index 7726739e401..3091153e1e6 100644 --- a/packages/server-renderer/__tests__/ssrScopeId.spec.ts +++ b/packages/server-renderer/__tests__/ssrScopeId.spec.ts @@ -1,4 +1,4 @@ -import { createApp, mergeProps, withCtx } from 'vue' +import { createApp, h, mergeProps, withCtx } from 'vue' import { renderToString } from '../src/renderToString' import { ssrRenderComponent, ssrRenderAttrs, ssrRenderSlot } from '../src' @@ -154,4 +154,29 @@ describe('ssr: scopedId runtime behavior', () => { `` ) }) + + // #3513 + test('scopeId inheritance across ssr-compiled andn on-ssr compiled parent chain', async () => { + const Child = { + ssrRender: (ctx: any, push: any, parent: any, attrs: any) => { + push(``) + } + } + + const Middle = { + render() { + return h(Child) + } + } + + const Comp = { + __scopeId: 'parent', + ssrRender: (ctx: any, push: any, parent: any) => { + push(ssrRenderComponent(Middle, null, null, parent)) + } + } + + const result = await renderToString(createApp(Comp)) // output: `
` + expect(result).toBe(`
`) + }) }) diff --git a/packages/server-renderer/src/render.ts b/packages/server-renderer/src/render.ts index 4f20e52e3f8..b3002ab2acf 100644 --- a/packages/server-renderer/src/render.ts +++ b/packages/server-renderer/src/render.ts @@ -129,13 +129,31 @@ function renderComponentSubTree( // resolve fallthrough attrs let attrs = instance.type.inheritAttrs !== false ? instance.attrs : undefined + let hasCloned = false - // inherited scopeId - const scopeId = instance.vnode.scopeId - if (scopeId || slotScopeId) { - attrs = { ...attrs } - if (scopeId) attrs[scopeId] = '' - if (slotScopeId) attrs[slotScopeId.trim()] = '' + let cur = instance + while (true) { + const scopeId = cur.vnode.scopeId + if (scopeId) { + if (!hasCloned) { + attrs = { ...attrs } + hasCloned = true + } + attrs![scopeId] = '' + } + const parent = cur.parent + if (parent && parent.subTree && parent.subTree === cur.vnode) { + // parent is a non-SSR compiled component and is rendering this + // component as root. inherit its scopeId if present. + cur = parent + } else { + break + } + } + + if (slotScopeId) { + if (!hasCloned) attrs = { ...attrs } + attrs![slotScopeId.trim()] = '' } // set current rendering instance for asset resolution