renderToString.ts 2.1 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182
  1. import {
  2. App,
  3. createApp,
  4. createVNode,
  5. ssrContextKey,
  6. ssrUtils,
  7. VNode
  8. } from 'vue'
  9. import { isPromise, isString } from '@vue/shared'
  10. import { SSRContext, renderComponentVNode, SSRBuffer } from './render'
  11. const { isVNode } = ssrUtils
  12. async function unrollBuffer(buffer: SSRBuffer): Promise<string> {
  13. if (buffer.hasAsync) {
  14. let ret = ''
  15. for (let i = 0; i < buffer.length; i++) {
  16. let item = buffer[i]
  17. if (isPromise(item)) {
  18. item = await item
  19. }
  20. if (isString(item)) {
  21. ret += item
  22. } else {
  23. ret += await unrollBuffer(item)
  24. }
  25. }
  26. return ret
  27. } else {
  28. // sync buffer can be more efficiently unrolled without unnecessary await
  29. // ticks
  30. return unrollBufferSync(buffer)
  31. }
  32. }
  33. function unrollBufferSync(buffer: SSRBuffer): string {
  34. let ret = ''
  35. for (let i = 0; i < buffer.length; i++) {
  36. let item = buffer[i]
  37. if (isString(item)) {
  38. ret += item
  39. } else {
  40. // since this is a sync buffer, child buffers are never promises
  41. ret += unrollBufferSync(item as SSRBuffer)
  42. }
  43. }
  44. return ret
  45. }
  46. export async function renderToString(
  47. input: App | VNode,
  48. context: SSRContext = {}
  49. ): Promise<string> {
  50. if (isVNode(input)) {
  51. // raw vnode, wrap with app (for context)
  52. return renderToString(createApp({ render: () => input }), context)
  53. }
  54. // rendering an app
  55. const vnode = createVNode(input._component, input._props)
  56. vnode.appContext = input._context
  57. // provide the ssr context to the tree
  58. input.provide(ssrContextKey, context)
  59. const buffer = await renderComponentVNode(vnode)
  60. await resolveTeleports(context)
  61. return unrollBuffer(buffer as SSRBuffer)
  62. }
  63. async function resolveTeleports(context: SSRContext) {
  64. if (context.__teleportBuffers) {
  65. context.teleports = context.teleports || {}
  66. for (const key in context.__teleportBuffers) {
  67. // note: it's OK to await sequentially here because the Promises were
  68. // created eagerly in parallel.
  69. context.teleports[key] = await unrollBuffer(
  70. (await Promise.all(context.__teleportBuffers[key])) as SSRBuffer
  71. )
  72. }
  73. }
  74. }