ssrRenderSlot.ts 2.2 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192
  1. import { ComponentInternalInstance, Slots } from 'vue'
  2. import { Props, PushFn, renderVNodeChildren, SSRBufferItem } from '../render'
  3. import { isArray } from '@vue/shared'
  4. export type SSRSlots = Record<string, SSRSlot>
  5. export type SSRSlot = (
  6. props: Props,
  7. push: PushFn,
  8. parentComponent: ComponentInternalInstance | null,
  9. scopeId: string | null
  10. ) => void
  11. export function ssrRenderSlot(
  12. slots: Slots | SSRSlots,
  13. slotName: string,
  14. slotProps: Props,
  15. fallbackRenderFn: (() => void) | null,
  16. push: PushFn,
  17. parentComponent: ComponentInternalInstance,
  18. slotScopeId?: string
  19. ) {
  20. // template-compiled slots are always rendered as fragments
  21. push(`<!--[-->`)
  22. ssrRenderSlotInner(
  23. slots,
  24. slotName,
  25. slotProps,
  26. fallbackRenderFn,
  27. push,
  28. parentComponent,
  29. slotScopeId
  30. )
  31. push(`<!--]-->`)
  32. }
  33. export function ssrRenderSlotInner(
  34. slots: Slots | SSRSlots,
  35. slotName: string,
  36. slotProps: Props,
  37. fallbackRenderFn: (() => void) | null,
  38. push: PushFn,
  39. parentComponent: ComponentInternalInstance,
  40. slotScopeId?: string
  41. ) {
  42. const slotFn = slots[slotName]
  43. if (slotFn) {
  44. const slotBuffer: SSRBufferItem[] = []
  45. const bufferedPush = (item: SSRBufferItem) => {
  46. slotBuffer.push(item)
  47. }
  48. const ret = slotFn(
  49. slotProps,
  50. bufferedPush,
  51. parentComponent,
  52. slotScopeId ? ' ' + slotScopeId : ''
  53. )
  54. if (isArray(ret)) {
  55. // normal slot
  56. renderVNodeChildren(push, ret, parentComponent, slotScopeId)
  57. } else {
  58. // ssr slot.
  59. // check if the slot renders all comments, in which case use the fallback
  60. let isEmptySlot = true
  61. for (let i = 0; i < slotBuffer.length; i++) {
  62. if (!isComment(slotBuffer[i])) {
  63. isEmptySlot = false
  64. break
  65. }
  66. }
  67. if (isEmptySlot) {
  68. if (fallbackRenderFn) {
  69. fallbackRenderFn()
  70. }
  71. } else {
  72. for (let i = 0; i < slotBuffer.length; i++) {
  73. push(slotBuffer[i])
  74. }
  75. }
  76. }
  77. } else if (fallbackRenderFn) {
  78. fallbackRenderFn()
  79. }
  80. }
  81. const commentRE = /<!--.*?-->/g
  82. function isComment(item: SSRBufferItem) {
  83. return (
  84. typeof item === 'string' &&
  85. commentRE.test(item) &&
  86. !item.replace(commentRE, '').trim()
  87. )
  88. }