Skip to content

Commit f12c81e

Browse files
committed
fix(compiler-ssr): fix hydration mismatch for conditional slot in transition
close #10743
1 parent c8e87a1 commit f12c81e

File tree

3 files changed

+44
-17
lines changed

3 files changed

+44
-17
lines changed

packages/compiler-core/src/transform.ts

+5
Original file line numberDiff line numberDiff line change
@@ -102,6 +102,9 @@ export interface TransformContext
102102
vOnce: number
103103
}
104104
parent: ParentNode | null
105+
// we could use a stack but in practice we've only ever needed two layers up
106+
// so this is more efficient
107+
grandParent: ParentNode | null
105108
childIndex: number
106109
currentNode: RootNode | TemplateChildNode | null
107110
inVOnce: boolean
@@ -193,6 +196,7 @@ export function createTransformContext(
193196
vOnce: 0,
194197
},
195198
parent: null,
199+
grandParent: null,
196200
currentNode: root,
197201
childIndex: 0,
198202
inVOnce: false,
@@ -401,6 +405,7 @@ export function traverseChildren(
401405
for (; i < parent.children.length; i++) {
402406
const child = parent.children[i]
403407
if (isString(child)) continue
408+
context.grandParent = context.parent
404409
context.parent = parent
405410
context.childIndex = i
406411
context.onNodeRemoved = nodeRemoved

packages/compiler-ssr/__tests__/ssrSlotOutlet.spec.ts

+16
Original file line numberDiff line numberDiff line change
@@ -143,4 +143,20 @@ describe('ssr: <slot>', () => {
143143
}"
144144
`)
145145
})
146+
147+
test('with v-if inside transition', () => {
148+
const { code } = compile(`<transition><slot v-if="true"/></transition>`)
149+
expect(code).toMatch(ssrHelpers[SSR_RENDER_SLOT_INNER])
150+
expect(code).toMatchInlineSnapshot(`
151+
"const { ssrRenderSlotInner: _ssrRenderSlotInner } = require("vue/server-renderer")
152+
153+
return function ssrRender(_ctx, _push, _parent, _attrs) {
154+
if (true) {
155+
_ssrRenderSlotInner(_ctx.$slots, "default", {}, null, _push, _parent, null, true)
156+
} else {
157+
_push(\`<!---->\`)
158+
}
159+
}"
160+
`)
161+
})
146162
})

packages/compiler-ssr/src/transforms/ssrTransformSlotOutlet.ts

+23-17
Original file line numberDiff line numberDiff line change
@@ -40,24 +40,30 @@ export const ssrTransformSlotOutlet: NodeTransform = (node, context) => {
4040

4141
// #3989, #9933
4242
// check if this is a single slot inside a transition wrapper - since
43-
// transition/transition-group will unwrap the slot fragment into vnode(s) at runtime,
44-
// we need to avoid rendering the slot as a fragment.
45-
const parent = context.parent
46-
let componentType
47-
if (
48-
parent &&
49-
parent.type === NodeTypes.ELEMENT &&
50-
parent.tagType === ElementTypes.COMPONENT &&
51-
((componentType = resolveComponentType(parent, context, true)) ===
52-
TRANSITION ||
53-
componentType === TRANSITION_GROUP) &&
54-
parent.children.filter(c => c.type === NodeTypes.ELEMENT).length === 1
55-
) {
56-
method = SSR_RENDER_SLOT_INNER
57-
if (!(context.scopeId && context.slotted !== false)) {
58-
args.push('null')
43+
// transition/transition-group will unwrap the slot fragment into vnode(s)
44+
// at runtime, we need to avoid rendering the slot as a fragment.
45+
let parent = context.parent!
46+
if (parent) {
47+
const children = parent.children
48+
// #10743 <slot v-if> in <Transition>
49+
if (parent.type === NodeTypes.IF_BRANCH) {
50+
parent = context.grandParent!
51+
}
52+
let componentType
53+
if (
54+
parent.type === NodeTypes.ELEMENT &&
55+
parent.tagType === ElementTypes.COMPONENT &&
56+
((componentType = resolveComponentType(parent, context, true)) ===
57+
TRANSITION ||
58+
componentType === TRANSITION_GROUP) &&
59+
children.filter(c => c.type === NodeTypes.ELEMENT).length === 1
60+
) {
61+
method = SSR_RENDER_SLOT_INNER
62+
if (!(context.scopeId && context.slotted !== false)) {
63+
args.push('null')
64+
}
65+
args.push('true')
5966
}
60-
args.push('true')
6167
}
6268

6369
node.ssrCodegenNode = createCallExpression(context.helper(method), args)

0 commit comments

Comments
 (0)