diff --git a/packages/runtime-core/__tests__/hydration.spec.ts b/packages/runtime-core/__tests__/hydration.spec.ts index 56011d06359..8daf68c27c8 100644 --- a/packages/runtime-core/__tests__/hydration.spec.ts +++ b/packages/runtime-core/__tests__/hydration.spec.ts @@ -2299,6 +2299,16 @@ describe('SSR hydration', () => { expect(`Hydration node mismatch`).not.toHaveBeenWarned() }) + test('comment mismatch (v-if)', () => { + const { container } = mountWithHydration(`<!--v-if-->`, () => + h('div', { 'data-allow-mismatch': '' }, [h('span', 'value')]), + ) + expect(container.innerHTML).toBe( + '<div data-allow-mismatch=""><span>value</span></div>', + ) + expect(`Hydration node mismatch`).not.toHaveBeenWarned() + }) + test('comment mismatch (text)', () => { const { container } = mountWithHydration( `<div data-allow-mismatch="children">foobar</div>`, diff --git a/packages/runtime-core/src/hydration.ts b/packages/runtime-core/src/hydration.ts index a94ff356810..3977b45a1d2 100644 --- a/packages/runtime-core/src/hydration.ts +++ b/packages/runtime-core/src/hydration.ts @@ -19,6 +19,7 @@ import { ShapeFlags, def, getEscapedCssVarName, + hasOwn, includeBooleanAttr, isBooleanAttr, isKnownHtmlAttr, @@ -675,7 +676,10 @@ export function createHydrationFunctions( slotScopeIds: string[] | null, isFragment: boolean, ): Node | null => { - if (!isMismatchAllowed(node.parentElement!, MismatchTypes.CHILDREN)) { + if ( + !isMismatchAllowed(node.parentElement!, MismatchTypes.CHILDREN) && + !isMismatchAllowedForCommentNode(node, vnode) + ) { ;(__DEV__ || __FEATURE_PROD_HYDRATION_MISMATCH_DETAILS__) && warn( `Hydration node mismatch:\n- rendered on server:`, @@ -993,3 +997,11 @@ function isMismatchAllowed( return allowedAttr.split(',').includes(MismatchTypeString[allowedType]) } } + +// data-allow-mismatch + v-if +function isMismatchAllowedForCommentNode( + node: Node, + { props }: VNode, +): boolean { + return isComment(node) && props != null && hasOwn(props, allowMismatchAttr) +}