ssrTransformSlotOutlet.ts 2.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596
  1. import {
  2. ElementTypes,
  3. type NodeTransform,
  4. NodeTypes,
  5. type SlotOutletNode,
  6. TRANSITION,
  7. TRANSITION_GROUP,
  8. createCallExpression,
  9. createFunctionExpression,
  10. isSlotOutlet,
  11. processSlotOutlet,
  12. resolveComponentType,
  13. } from '@vue/compiler-dom'
  14. import { SSR_RENDER_SLOT, SSR_RENDER_SLOT_INNER } from '../runtimeHelpers'
  15. import {
  16. type SSRTransformContext,
  17. processChildrenAsStatement,
  18. } from '../ssrCodegenTransform'
  19. export const ssrTransformSlotOutlet: NodeTransform = (node, context) => {
  20. if (isSlotOutlet(node)) {
  21. const { slotName, slotProps } = processSlotOutlet(node, context)
  22. const args = [
  23. `_ctx.$slots`,
  24. slotName,
  25. slotProps || `{}`,
  26. // fallback content placeholder. will be replaced in the process phase
  27. `null`,
  28. `_push`,
  29. `_parent`,
  30. ]
  31. // inject slot scope id if current template uses :slotted
  32. if (context.scopeId && context.slotted !== false) {
  33. args.push(`"${context.scopeId}-s"`)
  34. }
  35. let method = SSR_RENDER_SLOT
  36. // #3989, #9933
  37. // check if this is a single slot inside a transition wrapper - since
  38. // transition/transition-group will unwrap the slot fragment into vnode(s)
  39. // at runtime, we need to avoid rendering the slot as a fragment.
  40. let parent = context.parent!
  41. if (parent) {
  42. const children = parent.children
  43. // #10743 <slot v-if> in <Transition>
  44. if (parent.type === NodeTypes.IF_BRANCH) {
  45. parent = context.grandParent!
  46. }
  47. let componentType
  48. if (
  49. parent.type === NodeTypes.ELEMENT &&
  50. parent.tagType === ElementTypes.COMPONENT &&
  51. ((componentType = resolveComponentType(parent, context, true)) ===
  52. TRANSITION ||
  53. componentType === TRANSITION_GROUP) &&
  54. 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')
  59. }
  60. args.push('true')
  61. }
  62. }
  63. node.ssrCodegenNode = createCallExpression(context.helper(method), args)
  64. }
  65. }
  66. export function ssrProcessSlotOutlet(
  67. node: SlotOutletNode,
  68. context: SSRTransformContext,
  69. ): void {
  70. const renderCall = node.ssrCodegenNode!
  71. // has fallback content
  72. if (node.children.length) {
  73. const fallbackRenderFn = createFunctionExpression([])
  74. fallbackRenderFn.body = processChildrenAsStatement(node, context)
  75. // _renderSlot(slots, name, props, fallback, ...)
  76. renderCall.arguments[3] = fallbackRenderFn
  77. }
  78. // Forwarded <slot/>. Merge slot scope ids
  79. if (context.withSlotScopeId) {
  80. const slotScopeId = renderCall.arguments[6]
  81. renderCall.arguments[6] = slotScopeId
  82. ? `${slotScopeId as string} + _scopeId`
  83. : `_scopeId`
  84. }
  85. context.pushStatement(node.ssrCodegenNode!)
  86. }