diff --git a/packages/runtime-core/__tests__/hydration.spec.ts b/packages/runtime-core/__tests__/hydration.spec.ts index 17ffbb40092..493405b8ab8 100644 --- a/packages/runtime-core/__tests__/hydration.spec.ts +++ b/packages/runtime-core/__tests__/hydration.spec.ts @@ -982,6 +982,14 @@ describe('SSR hydration', () => { expect((container as any)._vnode).toBe(null) }) + // #6637 + test('stringified root fragment', () => { + mountWithHydration(`
`, () => + createStaticVNode(`
`, 1) + ) + expect(`mismatch`).not.toHaveBeenWarned() + }) + describe('mismatch handling', () => { test('text node', () => { const { container } = mountWithHydration(`foo`, () => 'bar') diff --git a/packages/runtime-core/src/hydration.ts b/packages/runtime-core/src/hydration.ts index 8ada97b166b..2170a9192cf 100644 --- a/packages/runtime-core/src/hydration.ts +++ b/packages/runtime-core/src/hydration.ts @@ -108,7 +108,7 @@ export function createHydrationFunctions( ) const { type, ref, shapeFlag, patchFlag } = vnode - const domType = node.nodeType + let domType = node.nodeType vnode.el = node if (patchFlag === PatchFlags.BAIL) { @@ -150,9 +150,12 @@ export function createHydrationFunctions( } break case Static: - if (domType !== DOMNodeTypes.ELEMENT && domType !== DOMNodeTypes.TEXT) { - nextNode = onMismatch() - } else { + if (isFragmentStart) { + // entire template is static but SSRed as a fragment + node = nextSibling(node)! + domType = node.nodeType + } + if (domType === DOMNodeTypes.ELEMENT || domType === DOMNodeTypes.TEXT) { // determine anchor, adopt content nextNode = node // if the static vnode has its content stripped during build, @@ -169,7 +172,9 @@ export function createHydrationFunctions( } nextNode = nextSibling(nextNode)! } - return nextNode + return isFragmentStart ? nextSibling(nextNode) : nextNode + } else { + onMismatch() } break case Fragment: