@@ -7,17 +7,21 @@ import {
7
7
VNode ,
8
8
createVNode
9
9
} from 'vue'
10
- import { isString } from '@vue/shared'
10
+ import { isString , isPromise , isArray } from '@vue/shared'
11
11
12
12
type SSRBuffer = SSRBufferItem [ ]
13
- type SSRBufferItem = string | Promise < SSRBuffer >
13
+ type SSRBufferItem = string | ResolvedSSRBuffer | Promise < SSRBuffer >
14
14
type ResolvedSSRBuffer = ( string | ResolvedSSRBuffer ) [ ]
15
15
16
- function createSSRBuffer ( ) {
16
+ function createBuffer ( ) {
17
17
let appendable = false
18
+ let hasAsync = false
18
19
const buffer : SSRBuffer = [ ]
19
20
return {
20
21
buffer,
22
+ hasAsync ( ) {
23
+ return hasAsync
24
+ } ,
21
25
push ( item : SSRBufferItem ) {
22
26
const isStringItem = isString ( item )
23
27
if ( appendable && isStringItem ) {
@@ -26,18 +30,14 @@ function createSSRBuffer() {
26
30
buffer . push ( item )
27
31
}
28
32
appendable = isStringItem
33
+ if ( ! isStringItem && ! isArray ( item ) ) {
34
+ // promise
35
+ hasAsync = true
36
+ }
29
37
}
30
38
}
31
39
}
32
40
33
- export async function renderToString ( app : App ) : Promise < string > {
34
- const resolvedBuffer = ( await renderComponent (
35
- app . _component ,
36
- app . _props
37
- ) ) as ResolvedSSRBuffer
38
- return unrollBuffer ( resolvedBuffer )
39
- }
40
-
41
41
function unrollBuffer ( buffer : ResolvedSSRBuffer ) : string {
42
42
let ret = ''
43
43
for ( let i = 0 ; i < buffer . length ; i ++ ) {
@@ -51,20 +51,35 @@ function unrollBuffer(buffer: ResolvedSSRBuffer): string {
51
51
return ret
52
52
}
53
53
54
- export async function renderComponent (
54
+ export async function renderToString ( app : App ) : Promise < string > {
55
+ const resolvedBuffer = ( await renderComponent (
56
+ app . _component ,
57
+ app . _props
58
+ ) ) as ResolvedSSRBuffer
59
+ return unrollBuffer ( resolvedBuffer )
60
+ }
61
+
62
+ export function renderComponent (
55
63
comp : Component ,
56
64
props : Record < string , any > | null = null ,
57
65
children : VNode [ 'children' ] = null ,
58
66
parentComponent : ComponentInternalInstance | null = null
59
- ) : Promise < SSRBuffer > {
60
- // 1. create component buffer
61
- const { buffer, push } = createSSRBuffer ( )
62
-
63
- // 2. create actual instance
67
+ ) : ResolvedSSRBuffer | Promise < SSRBuffer > {
64
68
const vnode = createVNode ( comp , props , children )
65
69
const instance = createComponentInstance ( vnode , parentComponent )
66
- await setupComponent ( instance , null )
70
+ const res = setupComponent ( instance , null )
71
+ if ( isPromise ( res ) ) {
72
+ return res . then ( ( ) => innerRenderComponent ( comp , instance ) )
73
+ } else {
74
+ return innerRenderComponent ( comp , instance )
75
+ }
76
+ }
67
77
78
+ function innerRenderComponent (
79
+ comp : Component ,
80
+ instance : ComponentInternalInstance
81
+ ) : ResolvedSSRBuffer | Promise < SSRBuffer > {
82
+ const { buffer, push, hasAsync } = createBuffer ( )
68
83
if ( typeof comp === 'function' ) {
69
84
// TODO FunctionalComponent
70
85
} else {
@@ -77,7 +92,11 @@ export async function renderComponent(
77
92
// TODO warn component missing render function
78
93
}
79
94
}
80
- // TS can't figure this out due to recursive occurance of Promise in type
81
- // @ts -ignore
82
- return Promise . all ( buffer )
95
+ // If the current component's buffer contains any Promise from async children,
96
+ // then it must return a Promise too. Otherwise this is a component that
97
+ // contains only sync children so we can avoid the async book-keeping overhead.
98
+ return hasAsync ( )
99
+ ? // TS can't figure out the typing due to recursive appearance of Promise
100
+ Promise . all ( buffer as any )
101
+ : ( buffer as ResolvedSSRBuffer )
83
102
}
0 commit comments