diff --git a/packages/compiler-core/src/transform.ts b/packages/compiler-core/src/transform.ts index 322d0e17638..69821f7f879 100644 --- a/packages/compiler-core/src/transform.ts +++ b/packages/compiler-core/src/transform.ts @@ -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 diff --git a/packages/compiler-ssr/__tests__/ssrSlotOutlet.spec.ts b/packages/compiler-ssr/__tests__/ssrSlotOutlet.spec.ts index 655d68efbbc..86863cfb85f 100644 --- a/packages/compiler-ssr/__tests__/ssrSlotOutlet.spec.ts +++ b/packages/compiler-ssr/__tests__/ssrSlotOutlet.spec.ts @@ -143,4 +143,20 @@ describe('ssr: ', () => { }" `) }) + + test('with v-if inside transition', () => { + const { code } = compile(``) + 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(\`\`) + } + }" + `) + }) }) diff --git a/packages/compiler-ssr/src/transforms/ssrTransformSlotOutlet.ts b/packages/compiler-ssr/src/transforms/ssrTransformSlotOutlet.ts index ad08a23a445..f4e6dba209d 100644 --- a/packages/compiler-ssr/src/transforms/ssrTransformSlotOutlet.ts +++ b/packages/compiler-ssr/src/transforms/ssrTransformSlotOutlet.ts @@ -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 in + 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)