Skip to content

Commit

Permalink
fix(compiler-ssr): fix hydration mismatch for conditional slot in tra…
Browse files Browse the repository at this point in the history
…nsition

close #10743
  • Loading branch information
yyx990803 committed Apr 22, 2024
1 parent c8e87a1 commit f12c81e
Show file tree
Hide file tree
Showing 3 changed files with 44 additions and 17 deletions.
5 changes: 5 additions & 0 deletions packages/compiler-core/src/transform.ts
Expand Up @@ -102,6 +102,9 @@ export interface TransformContext
vOnce: number
}
parent: ParentNode | null
// we could use a stack but in practice we've only ever needed two layers up
// so this is more efficient
grandParent: ParentNode | null
childIndex: number
currentNode: RootNode | TemplateChildNode | null
inVOnce: boolean
Expand Down Expand Up @@ -193,6 +196,7 @@ export function createTransformContext(
vOnce: 0,
},
parent: null,
grandParent: null,
currentNode: root,
childIndex: 0,
inVOnce: false,
Expand Down Expand Up @@ -401,6 +405,7 @@ export function traverseChildren(
for (; i < parent.children.length; i++) {
const child = parent.children[i]
if (isString(child)) continue
context.grandParent = context.parent
context.parent = parent
context.childIndex = i
context.onNodeRemoved = nodeRemoved
Expand Down
16 changes: 16 additions & 0 deletions packages/compiler-ssr/__tests__/ssrSlotOutlet.spec.ts
Expand Up @@ -143,4 +143,20 @@ describe('ssr: <slot>', () => {
}"
`)
})

test('with v-if inside transition', () => {
const { code } = compile(`<transition><slot v-if="true"/></transition>`)
expect(code).toMatch(ssrHelpers[SSR_RENDER_SLOT_INNER])
expect(code).toMatchInlineSnapshot(`
"const { ssrRenderSlotInner: _ssrRenderSlotInner } = require("vue/server-renderer")
return function ssrRender(_ctx, _push, _parent, _attrs) {
if (true) {
_ssrRenderSlotInner(_ctx.$slots, "default", {}, null, _push, _parent, null, true)
} else {
_push(\`<!---->\`)
}
}"
`)
})
})
40 changes: 23 additions & 17 deletions packages/compiler-ssr/src/transforms/ssrTransformSlotOutlet.ts
Expand Up @@ -40,24 +40,30 @@ export const ssrTransformSlotOutlet: NodeTransform = (node, context) => {

// #3989, #9933
// check if this is a single slot inside a transition wrapper - since
// transition/transition-group will unwrap the slot fragment into vnode(s) at runtime,
// we need to avoid rendering the slot as a fragment.
const parent = context.parent
let componentType
if (
parent &&
parent.type === NodeTypes.ELEMENT &&
parent.tagType === ElementTypes.COMPONENT &&
((componentType = resolveComponentType(parent, context, true)) ===
TRANSITION ||
componentType === TRANSITION_GROUP) &&
parent.children.filter(c => c.type === NodeTypes.ELEMENT).length === 1
) {
method = SSR_RENDER_SLOT_INNER
if (!(context.scopeId && context.slotted !== false)) {
args.push('null')
// transition/transition-group will unwrap the slot fragment into vnode(s)
// at runtime, we need to avoid rendering the slot as a fragment.
let parent = context.parent!
if (parent) {
const children = parent.children
// #10743 <slot v-if> in <Transition>
if (parent.type === NodeTypes.IF_BRANCH) {
parent = context.grandParent!
}
let componentType
if (
parent.type === NodeTypes.ELEMENT &&
parent.tagType === ElementTypes.COMPONENT &&
((componentType = resolveComponentType(parent, context, true)) ===
TRANSITION ||
componentType === TRANSITION_GROUP) &&
children.filter(c => c.type === NodeTypes.ELEMENT).length === 1
) {
method = SSR_RENDER_SLOT_INNER
if (!(context.scopeId && context.slotted !== false)) {
args.push('null')
}
args.push('true')
}
args.push('true')
}

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

0 comments on commit f12c81e

Please sign in to comment.