Skip to content

Commit 30584bc

Browse files
committed
perf(ssr): avoid unnecessary await ticks when unrolling sync buffers
1 parent 6bc0e0a commit 30584bc

File tree

3 files changed

+54
-10
lines changed

3 files changed

+54
-10
lines changed

packages/server-renderer/src/render.ts

+8-2
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,8 @@ import {
2020
isPromise,
2121
isString,
2222
isVoidTag,
23-
ShapeFlags
23+
ShapeFlags,
24+
isArray
2425
} from '@vue/shared'
2526
import { ssrRenderAttrs } from './helpers/ssrRenderAttrs'
2627
import { ssrCompile } from './helpers/ssrCompile'
@@ -35,7 +36,7 @@ const {
3536
normalizeSuspenseChildren
3637
} = ssrUtils
3738

38-
export type SSRBuffer = SSRBufferItem[]
39+
export type SSRBuffer = SSRBufferItem[] & { hasAsync?: boolean }
3940
export type SSRBufferItem = string | SSRBuffer | Promise<SSRBuffer>
4041
export type PushFn = (item: SSRBufferItem) => void
4142
export type Props = Record<string, unknown>
@@ -68,6 +69,11 @@ export function createBuffer() {
6869
buffer.push(item)
6970
}
7071
appendable = isStringItem
72+
if (isPromise(item) || (isArray(item) && item.hasAsync)) {
73+
// promise, or child buffer with async, mark as async.
74+
// this allows skipping unnecessary await ticks during unroll stage
75+
buffer.hasAsync = true
76+
}
7177
}
7278
}
7379
}

packages/server-renderer/src/renderToStream.ts

+22-4
Original file line numberDiff line numberDiff line change
@@ -16,15 +16,33 @@ async function unrollBuffer(
1616
buffer: SSRBuffer,
1717
stream: Readable
1818
): Promise<void> {
19+
if (buffer.hasAsync) {
20+
for (let i = 0; i < buffer.length; i++) {
21+
let item = buffer[i]
22+
if (isPromise(item)) {
23+
item = await item
24+
}
25+
if (isString(item)) {
26+
stream.push(item)
27+
} else {
28+
await unrollBuffer(item, stream)
29+
}
30+
}
31+
} else {
32+
// sync buffer can be more efficiently unrolled without unnecessary await
33+
// ticks
34+
unrollBufferSync(buffer, stream)
35+
}
36+
}
37+
38+
function unrollBufferSync(buffer: SSRBuffer, stream: Readable) {
1939
for (let i = 0; i < buffer.length; i++) {
2040
let item = buffer[i]
21-
if (isPromise(item)) {
22-
item = await item
23-
}
2441
if (isString(item)) {
2542
stream.push(item)
2643
} else {
27-
await unrollBuffer(item, stream)
44+
// since this is a sync buffer, child buffers are never promises
45+
unrollBufferSync(item as SSRBuffer, stream)
2846
}
2947
}
3048
}

packages/server-renderer/src/renderToString.ts

+24-4
Original file line numberDiff line numberDiff line change
@@ -12,16 +12,36 @@ import { SSRContext, renderComponentVNode, SSRBuffer } from './render'
1212
const { isVNode } = ssrUtils
1313

1414
async function unrollBuffer(buffer: SSRBuffer): Promise<string> {
15+
if (buffer.hasAsync) {
16+
let ret = ''
17+
for (let i = 0; i < buffer.length; i++) {
18+
let item = buffer[i]
19+
if (isPromise(item)) {
20+
item = await item
21+
}
22+
if (isString(item)) {
23+
ret += item
24+
} else {
25+
ret += await unrollBuffer(item)
26+
}
27+
}
28+
return ret
29+
} else {
30+
// sync buffer can be more efficiently unrolled without unnecessary await
31+
// ticks
32+
return unrollBufferSync(buffer)
33+
}
34+
}
35+
36+
function unrollBufferSync(buffer: SSRBuffer): string {
1537
let ret = ''
1638
for (let i = 0; i < buffer.length; i++) {
1739
let item = buffer[i]
18-
if (isPromise(item)) {
19-
item = await item
20-
}
2140
if (isString(item)) {
2241
ret += item
2342
} else {
24-
ret += await unrollBuffer(item as SSRBuffer)
43+
// since this is a sync buffer, child buffers are never promises
44+
ret += unrollBufferSync(item as SSRBuffer)
2545
}
2646
}
2747
return ret

0 commit comments

Comments
 (0)