import { compile } from '../src'
+import { ssrHelpers, SSR_RENDER_SLOT_INNER } from '../src/runtimeHelpers'
describe('ssr: <slot>', () => {
test('basic', () => {
}"
`)
})
+
+ test('inside transition', () => {
+ const { code } = compile(`<transition><slot/></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) {
+ _ssrRenderSlotInner(_ctx.$slots, \\"default\\", {}, null, _push, _parent)
+ }"
+ `)
+ })
})
export const SSR_RENDER_VNODE = Symbol(`ssrRenderVNode`)
export const SSR_RENDER_COMPONENT = Symbol(`ssrRenderComponent`)
export const SSR_RENDER_SLOT = Symbol(`ssrRenderSlot`)
+export const SSR_RENDER_SLOT_INNER = Symbol(`ssrRenderSlotInner`)
export const SSR_RENDER_CLASS = Symbol(`ssrRenderClass`)
export const SSR_RENDER_STYLE = Symbol(`ssrRenderStyle`)
export const SSR_RENDER_ATTRS = Symbol(`ssrRenderAttrs`)
[SSR_RENDER_VNODE]: `ssrRenderVNode`,
[SSR_RENDER_COMPONENT]: `ssrRenderComponent`,
[SSR_RENDER_SLOT]: `ssrRenderSlot`,
+ [SSR_RENDER_SLOT_INNER]: `ssrRenderSlotInner`,
[SSR_RENDER_CLASS]: `ssrRenderClass`,
[SSR_RENDER_STYLE]: `ssrRenderStyle`,
[SSR_RENDER_ATTRS]: `ssrRenderAttrs`,
processSlotOutlet,
createCallExpression,
SlotOutletNode,
- createFunctionExpression
+ createFunctionExpression,
+ NodeTypes,
+ ElementTypes,
+ resolveComponentType,
+ TRANSITION
} from '@vue/compiler-dom'
-import { SSR_RENDER_SLOT } from '../runtimeHelpers'
+import { SSR_RENDER_SLOT, SSR_RENDER_SLOT_INNER } from '../runtimeHelpers'
import {
SSRTransformContext,
processChildrenAsStatement
args.push(`"${context.scopeId}-s"`)
}
- node.ssrCodegenNode = createCallExpression(
- context.helper(SSR_RENDER_SLOT),
- args
- )
+ let method = SSR_RENDER_SLOT
+
+ // #3989
+ // check if this is a single slot inside a transition wrapper - since
+ // transition will unwrap the slot fragment into a single vnode at runtime,
+ // we need to avoid rendering the slot as a fragment.
+ const parent = context.parent
+ if (
+ parent &&
+ parent.type === NodeTypes.ELEMENT &&
+ parent.tagType === ElementTypes.COMPONENT &&
+ resolveComponentType(parent, context, true) === TRANSITION &&
+ parent.children.filter(c => c.type === NodeTypes.ELEMENT).length === 1
+ ) {
+ method = SSR_RENDER_SLOT_INNER
+ }
+
+ node.ssrCodegenNode = createCallExpression(context.helper(method), args)
}
}
nextNode = onMismatch()
} else {
if ((node as Text).data !== vnode.children) {
- hasMismatch = true
+ hasMismatch = true; debugger
__DEV__ &&
warn(
`Hydration text mismatch:` +
)
let hasWarned = false
while (next) {
- hasMismatch = true
+ hasMismatch = true; debugger
if (__DEV__ && !hasWarned) {
warn(
`Hydration children mismatch in <${vnode.type as string}>: ` +
}
} else if (shapeFlag & ShapeFlags.TEXT_CHILDREN) {
if (el.textContent !== vnode.children) {
- hasMismatch = true
+ hasMismatch = true; debugger
__DEV__ &&
warn(
`Hydration text content mismatch in <${
} else if (vnode.type === Text && !vnode.children) {
continue
} else {
- hasMismatch = true
+ hasMismatch = true; debugger
if (__DEV__ && !hasWarned) {
warn(
`Hydration children mismatch in <${container.tagName.toLowerCase()}>: ` +
} else {
// fragment didn't hydrate successfully, since we didn't get a end anchor
// back. This should have led to node/children mismatch warnings.
- hasMismatch = true
+ hasMismatch = true; debugger
// since the anchor is missing, we need to create one and insert it
insert((vnode.anchor = createComment(`]`)), container, next)
return next
slotScopeIds: string[] | null,
isFragment: boolean
): Node | null => {
- hasMismatch = true
+ hasMismatch = true; debugger
__DEV__ &&
warn(
`Hydration node mismatch:\n- Client vnode:`,
) {
// template-compiled slots are always rendered as fragments
push(`<!--[-->`)
+ ssrRenderSlotInner(
+ slots,
+ slotName,
+ slotProps,
+ fallbackRenderFn,
+ push,
+ parentComponent,
+ slotScopeId
+ )
+ push(`<!--]-->`)
+}
+
+export function ssrRenderSlotInner(
+ slots: Slots | SSRSlots,
+ slotName: string,
+ slotProps: Props,
+ fallbackRenderFn: (() => void) | null,
+ push: PushFn,
+ parentComponent: ComponentInternalInstance,
+ slotScopeId?: string
+) {
const slotFn = slots[slotName]
if (slotFn) {
const slotBuffer: SSRBufferItem[] = []
} else if (fallbackRenderFn) {
fallbackRenderFn()
}
- push(`<!--]-->`)
}
const commentRE = /^<!--.*-->$/
// internal runtime helpers
export { renderVNode as ssrRenderVNode } from './render'
export { ssrRenderComponent } from './helpers/ssrRenderComponent'
-export { ssrRenderSlot } from './helpers/ssrRenderSlot'
+export { ssrRenderSlot, ssrRenderSlotInner } from './helpers/ssrRenderSlot'
export { ssrRenderTeleport } from './helpers/ssrRenderTeleport'
export {
ssrRenderClass,