From: edison Date: Wed, 23 Jul 2025 00:42:34 +0000 (+0800) Subject: fix(ssr): ensure empty slots render as a comment node in Transition (#13396) X-Git-Tag: v3.5.18~2 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=8cfc10a80b9cbf5d801ab149e49b8506d192e7e1;p=thirdparty%2Fvuejs%2Fcore.git fix(ssr): ensure empty slots render as a comment node in Transition (#13396) close #13394 --- diff --git a/packages/runtime-core/__tests__/hydration.spec.ts b/packages/runtime-core/__tests__/hydration.spec.ts index 43af0583ce..6828e61ec5 100644 --- a/packages/runtime-core/__tests__/hydration.spec.ts +++ b/packages/runtime-core/__tests__/hydration.spec.ts @@ -1740,6 +1740,35 @@ describe('SSR hydration', () => { expect(`mismatch`).not.toHaveBeenWarned() }) + // #13394 + test('transition appear work with empty content', async () => { + const show = ref(true) + const { vnode, container } = mountWithHydration( + ``, + function (this: any) { + return h( + Transition, + { appear: true }, + { + default: () => + show.value + ? renderSlot(this.$slots, 'default') + : createTextVNode('foo'), + }, + ) + }, + ) + + // empty slot render as a comment node + expect(container.firstChild!.nodeType).toBe(Node.COMMENT_NODE) + expect(vnode.el).toBe(container.firstChild) + expect(`mismatch`).not.toHaveBeenWarned() + + show.value = false + await nextTick() + expect(container.innerHTML).toBe('foo') + }) + test('transition appear with v-if', () => { const show = false const { vnode, container } = mountWithHydration( diff --git a/packages/server-renderer/__tests__/ssrSlot.spec.ts b/packages/server-renderer/__tests__/ssrSlot.spec.ts index 4cc7fd97ef..214e6ee840 100644 --- a/packages/server-renderer/__tests__/ssrSlot.spec.ts +++ b/packages/server-renderer/__tests__/ssrSlot.spec.ts @@ -111,26 +111,106 @@ describe('ssr: slot', () => { }) test('transition slot', async () => { + const ReusableTransition = { + template: ``, + } + + const ReusableTransitionWithAppear = { + template: ``, + } + expect( await renderToString( createApp({ components: { - one: { - template: ``, - }, + one: ReusableTransition, }, template: `
foo
`, }), ), ).toBe(``) + expect(await renderToString(createApp(ReusableTransition))).toBe(``) + + expect(await renderToString(createApp(ReusableTransitionWithAppear))).toBe( + ``, + ) + expect( await renderToString( createApp({ components: { - one: { - template: ``, - }, + one: ReusableTransition, + }, + template: ``, + }), + ), + ).toBe(``) + + expect( + await renderToString( + createApp({ + components: { + one: ReusableTransitionWithAppear, + }, + template: ``, + }), + ), + ).toBe(``) + + expect( + await renderToString( + createApp({ + render() { + return h(ReusableTransition, null, { + default: () => null, + }) + }, + }), + ), + ).toBe(``) + + expect( + await renderToString( + createApp({ + render() { + return h(ReusableTransitionWithAppear, null, { + default: () => null, + }) + }, + }), + ), + ).toBe(``) + + expect( + await renderToString( + createApp({ + render() { + return h(ReusableTransitionWithAppear, null, { + default: () => [], + }) + }, + }), + ), + ).toBe(``) + + expect( + await renderToString( + createApp({ + render() { + return h(ReusableTransition, null, { + default: () => [], + }) + }, + }), + ), + ).toBe(``) + + expect( + await renderToString( + createApp({ + components: { + one: ReusableTransition, }, template: `
foo
`, }), diff --git a/packages/server-renderer/src/helpers/ssrRenderSlot.ts b/packages/server-renderer/src/helpers/ssrRenderSlot.ts index 19aa4ce63b..2f93a12de9 100644 --- a/packages/server-renderer/src/helpers/ssrRenderSlot.ts +++ b/packages/server-renderer/src/helpers/ssrRenderSlot.ts @@ -74,6 +74,8 @@ export function ssrRenderSlotInner( ) } else if (fallbackRenderFn) { fallbackRenderFn() + } else if (transition) { + push(``) } } else { // ssr slot. @@ -110,13 +112,19 @@ export function ssrRenderSlotInner( end-- } - for (let i = start; i < end; i++) { - push(slotBuffer[i]) + if (start < end) { + for (let i = start; i < end; i++) { + push(slotBuffer[i]) + } + } else if (transition) { + push(``) } } } } else if (fallbackRenderFn) { fallbackRenderFn() + } else if (transition) { + push(``) } }