Skip to content

Commit 297282a

Browse files
committed
perf(ssr): avoid unnecessary async overhead
1 parent 8c892e0 commit 297282a

File tree

2 files changed

+43
-22
lines changed

2 files changed

+43
-22
lines changed

packages/runtime-core/src/component.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -337,7 +337,9 @@ function setupStatefulComponent(
337337
if (isPromise(setupResult)) {
338338
if (__SSR__) {
339339
// return the promise so server-renderer can wait on it
340-
return setupResult
340+
return setupResult.then(resolvedResult => {
341+
handleSetupResult(instance, resolvedResult, parentSuspense)
342+
})
341343
} else if (__FEATURE_SUSPENSE__) {
342344
// async setup returned Promise.
343345
// bail here and wait for re-entry.

packages/server-renderer/src/index.ts

Lines changed: 40 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -7,17 +7,21 @@ import {
77
VNode,
88
createVNode
99
} from 'vue'
10-
import { isString } from '@vue/shared'
10+
import { isString, isPromise, isArray } from '@vue/shared'
1111

1212
type SSRBuffer = SSRBufferItem[]
13-
type SSRBufferItem = string | Promise<SSRBuffer>
13+
type SSRBufferItem = string | ResolvedSSRBuffer | Promise<SSRBuffer>
1414
type ResolvedSSRBuffer = (string | ResolvedSSRBuffer)[]
1515

16-
function createSSRBuffer() {
16+
function createBuffer() {
1717
let appendable = false
18+
let hasAsync = false
1819
const buffer: SSRBuffer = []
1920
return {
2021
buffer,
22+
hasAsync() {
23+
return hasAsync
24+
},
2125
push(item: SSRBufferItem) {
2226
const isStringItem = isString(item)
2327
if (appendable && isStringItem) {
@@ -26,18 +30,14 @@ function createSSRBuffer() {
2630
buffer.push(item)
2731
}
2832
appendable = isStringItem
33+
if (!isStringItem && !isArray(item)) {
34+
// promise
35+
hasAsync = true
36+
}
2937
}
3038
}
3139
}
3240

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-
4141
function unrollBuffer(buffer: ResolvedSSRBuffer): string {
4242
let ret = ''
4343
for (let i = 0; i < buffer.length; i++) {
@@ -51,20 +51,35 @@ function unrollBuffer(buffer: ResolvedSSRBuffer): string {
5151
return ret
5252
}
5353

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(
5563
comp: Component,
5664
props: Record<string, any> | null = null,
5765
children: VNode['children'] = null,
5866
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> {
6468
const vnode = createVNode(comp, props, children)
6569
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+
}
6777

78+
function innerRenderComponent(
79+
comp: Component,
80+
instance: ComponentInternalInstance
81+
): ResolvedSSRBuffer | Promise<SSRBuffer> {
82+
const { buffer, push, hasAsync } = createBuffer()
6883
if (typeof comp === 'function') {
6984
// TODO FunctionalComponent
7085
} else {
@@ -77,7 +92,11 @@ export async function renderComponent(
7792
// TODO warn component missing render function
7893
}
7994
}
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)
83102
}

0 commit comments

Comments
 (0)