fix(compiler-ssr): fix hydration mismatch for conditional slot in transition
close #10743
This commit is contained in:
parent
c8e87a1c90
commit
f12c81efca
|
@ -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
|
||||
|
@ -193,6 +196,7 @@ export function createTransformContext(
|
|||
vOnce: 0,
|
||||
},
|
||||
parent: null,
|
||||
grandParent: null,
|
||||
currentNode: root,
|
||||
childIndex: 0,
|
||||
inVOnce: false,
|
||||
|
@ -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
|
||||
|
|
|
@ -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,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)
|
||||
|
|
Loading…
Reference in New Issue