ssrTransformTransitionGroup.ts 3.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118
  1. import {
  2. type AttributeNode,
  3. type ComponentNode,
  4. type DirectiveNode,
  5. type JSChildNode,
  6. NodeTypes,
  7. type TransformContext,
  8. buildProps,
  9. createCallExpression,
  10. findProp,
  11. } from '@vue/compiler-dom'
  12. import { SSR_RENDER_ATTRS } from '../runtimeHelpers'
  13. import {
  14. type SSRTransformContext,
  15. processChildren,
  16. } from '../ssrCodegenTransform'
  17. import { buildSSRProps } from './ssrTransformElement'
  18. const wipMap = new WeakMap<ComponentNode, WIPEntry>()
  19. interface WIPEntry {
  20. tag: AttributeNode | DirectiveNode
  21. propsExp: string | JSChildNode | null
  22. scopeId: string | null
  23. }
  24. // phase 1: build props
  25. export function ssrTransformTransitionGroup(
  26. node: ComponentNode,
  27. context: TransformContext,
  28. ) {
  29. return () => {
  30. const tag = findProp(node, 'tag')
  31. if (tag) {
  32. const otherProps = node.props.filter(p => p !== tag)
  33. const { props, directives } = buildProps(
  34. node,
  35. context,
  36. otherProps,
  37. true /* isComponent */,
  38. false /* isDynamicComponent */,
  39. true /* ssr (skip event listeners) */,
  40. )
  41. let propsExp = null
  42. if (props || directives.length) {
  43. propsExp = createCallExpression(context.helper(SSR_RENDER_ATTRS), [
  44. buildSSRProps(props, directives, context),
  45. ])
  46. }
  47. wipMap.set(node, {
  48. tag,
  49. propsExp,
  50. scopeId: context.scopeId || null,
  51. })
  52. }
  53. }
  54. }
  55. // phase 2: process children
  56. export function ssrProcessTransitionGroup(
  57. node: ComponentNode,
  58. context: SSRTransformContext,
  59. ) {
  60. const entry = wipMap.get(node)
  61. if (entry) {
  62. const { tag, propsExp, scopeId } = entry
  63. if (tag.type === NodeTypes.DIRECTIVE) {
  64. // dynamic :tag
  65. context.pushStringPart(`<`)
  66. context.pushStringPart(tag.exp!)
  67. if (propsExp) {
  68. context.pushStringPart(propsExp)
  69. }
  70. if (scopeId) {
  71. context.pushStringPart(` ${scopeId}`)
  72. }
  73. context.pushStringPart(`>`)
  74. processChildren(
  75. node,
  76. context,
  77. false,
  78. /**
  79. * TransitionGroup has the special runtime behavior of flattening and
  80. * concatenating all children into a single fragment (in order for them to
  81. * be patched using the same key map) so we need to account for that here
  82. * by disabling nested fragment wrappers from being generated.
  83. */
  84. true,
  85. /**
  86. * TransitionGroup filters out comment children at runtime and thus
  87. * doesn't expect comments to be present during hydration. We need to
  88. * account for that by disabling the empty comment that is otherwise
  89. * rendered for a falsy v-if that has no v-else specified. (#6715)
  90. */
  91. true,
  92. )
  93. context.pushStringPart(`</`)
  94. context.pushStringPart(tag.exp!)
  95. context.pushStringPart(`>`)
  96. } else {
  97. // static tag
  98. context.pushStringPart(`<${tag.value!.content}`)
  99. if (propsExp) {
  100. context.pushStringPart(propsExp)
  101. }
  102. if (scopeId) {
  103. context.pushStringPart(` ${scopeId}`)
  104. }
  105. context.pushStringPart(`>`)
  106. processChildren(node, context, false, true)
  107. context.pushStringPart(`</${tag.value!.content}>`)
  108. }
  109. } else {
  110. // fragment
  111. processChildren(node, context, true, true, true)
  112. }
  113. }