renderSlot.ts 3.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118
  1. import type { RawSlots, Slots } from '../componentSlots'
  2. import {
  3. type ContextualRenderFn,
  4. currentRenderingInstance,
  5. } from '../componentRenderContext'
  6. import {
  7. Comment,
  8. Fragment,
  9. type VNode,
  10. type VNodeArrayChildren,
  11. createBlock,
  12. createVNode,
  13. isVNode,
  14. openBlock,
  15. } from '../vnode'
  16. import { PatchFlags, SlotFlags, isSymbol } from '@vue/shared'
  17. import { warn } from '../warning'
  18. import { isAsyncWrapper } from '../apiAsyncComponent'
  19. import type { Data } from '../component'
  20. /**
  21. * Compiler runtime helper for rendering `<slot/>`
  22. * @private
  23. */
  24. export function renderSlot(
  25. slots: Slots,
  26. name: string,
  27. props: Data = {},
  28. // this is not a user-facing function, so the fallback is always generated by
  29. // the compiler and guaranteed to be a function returning an array
  30. fallback?: () => VNodeArrayChildren,
  31. noSlotted?: boolean,
  32. ): VNode {
  33. if (
  34. currentRenderingInstance &&
  35. (currentRenderingInstance.ce ||
  36. (currentRenderingInstance.parent &&
  37. isAsyncWrapper(currentRenderingInstance.parent) &&
  38. currentRenderingInstance.parent.ce))
  39. ) {
  40. // in custom element mode, render <slot/> as actual slot outlets
  41. // wrap it with a fragment because in shadowRoot: false mode the slot
  42. // element gets replaced by injected content
  43. if (name !== 'default') props.name = name
  44. return (
  45. openBlock(),
  46. createBlock(
  47. Fragment,
  48. null,
  49. [createVNode('slot', props, fallback && fallback())],
  50. PatchFlags.STABLE_FRAGMENT,
  51. )
  52. )
  53. }
  54. let slot = slots[name]
  55. if (__DEV__ && slot && slot.length > 1) {
  56. warn(
  57. `SSR-optimized slot function detected in a non-SSR-optimized render ` +
  58. `function. You need to mark this component with $dynamic-slots in the ` +
  59. `parent template.`,
  60. )
  61. slot = () => []
  62. }
  63. // a compiled slot disables block tracking by default to avoid manual
  64. // invocation interfering with template-based block tracking, but in
  65. // `renderSlot` we can be sure that it's template-based so we can force
  66. // enable it.
  67. if (slot && (slot as ContextualRenderFn)._c) {
  68. ;(slot as ContextualRenderFn)._d = false
  69. }
  70. openBlock()
  71. const validSlotContent = slot && ensureValidVNode(slot(props))
  72. const slotKey =
  73. props.key ||
  74. // slot content array of a dynamic conditional slot may have a branch
  75. // key attached in the `createSlots` helper, respect that
  76. (validSlotContent && (validSlotContent as any).key)
  77. const rendered = createBlock(
  78. Fragment,
  79. {
  80. key:
  81. (slotKey && !isSymbol(slotKey) ? slotKey : `_${name}`) +
  82. // #7256 force differentiate fallback content from actual content
  83. (!validSlotContent && fallback ? '_fb' : ''),
  84. },
  85. validSlotContent || (fallback ? fallback() : []),
  86. validSlotContent && (slots as RawSlots)._ === SlotFlags.STABLE
  87. ? PatchFlags.STABLE_FRAGMENT
  88. : PatchFlags.BAIL,
  89. )
  90. if (!noSlotted && rendered.scopeId) {
  91. rendered.slotScopeIds = [rendered.scopeId + '-s']
  92. }
  93. if (slot && (slot as ContextualRenderFn)._c) {
  94. ;(slot as ContextualRenderFn)._d = true
  95. }
  96. return rendered
  97. }
  98. export function ensureValidVNode(
  99. vnodes: VNodeArrayChildren,
  100. ): VNodeArrayChildren | null {
  101. return vnodes.some(child => {
  102. if (!isVNode(child)) return true
  103. if (child.type === Comment) return false
  104. if (
  105. child.type === Fragment &&
  106. !ensureValidVNode(child.children as VNodeArrayChildren)
  107. )
  108. return false
  109. return true
  110. })
  111. ? vnodes
  112. : null
  113. }