perf(ssr): avoid unnecessary await ticks when unrolling sync buffers

This commit is contained in:
Evan You 2020-06-26 11:10:30 -04:00
parent 6bc0e0a31a
commit 30584bcc61
3 changed files with 54 additions and 10 deletions

View File

@ -20,7 +20,8 @@ import {
isPromise, isPromise,
isString, isString,
isVoidTag, isVoidTag,
ShapeFlags ShapeFlags,
isArray
} from '@vue/shared' } from '@vue/shared'
import { ssrRenderAttrs } from './helpers/ssrRenderAttrs' import { ssrRenderAttrs } from './helpers/ssrRenderAttrs'
import { ssrCompile } from './helpers/ssrCompile' import { ssrCompile } from './helpers/ssrCompile'
@ -35,7 +36,7 @@ const {
normalizeSuspenseChildren normalizeSuspenseChildren
} = ssrUtils } = ssrUtils
export type SSRBuffer = SSRBufferItem[] export type SSRBuffer = SSRBufferItem[] & { hasAsync?: boolean }
export type SSRBufferItem = string | SSRBuffer | Promise<SSRBuffer> export type SSRBufferItem = string | SSRBuffer | Promise<SSRBuffer>
export type PushFn = (item: SSRBufferItem) => void export type PushFn = (item: SSRBufferItem) => void
export type Props = Record<string, unknown> export type Props = Record<string, unknown>
@ -68,6 +69,11 @@ export function createBuffer() {
buffer.push(item) buffer.push(item)
} }
appendable = isStringItem appendable = isStringItem
if (isPromise(item) || (isArray(item) && item.hasAsync)) {
// promise, or child buffer with async, mark as async.
// this allows skipping unnecessary await ticks during unroll stage
buffer.hasAsync = true
}
} }
} }
} }

View File

@ -16,15 +16,33 @@ async function unrollBuffer(
buffer: SSRBuffer, buffer: SSRBuffer,
stream: Readable stream: Readable
): Promise<void> { ): Promise<void> {
if (buffer.hasAsync) {
for (let i = 0; i < buffer.length; i++) {
let item = buffer[i]
if (isPromise(item)) {
item = await item
}
if (isString(item)) {
stream.push(item)
} else {
await unrollBuffer(item, stream)
}
}
} else {
// sync buffer can be more efficiently unrolled without unnecessary await
// ticks
unrollBufferSync(buffer, stream)
}
}
function unrollBufferSync(buffer: SSRBuffer, stream: Readable) {
for (let i = 0; i < buffer.length; i++) { for (let i = 0; i < buffer.length; i++) {
let item = buffer[i] let item = buffer[i]
if (isPromise(item)) {
item = await item
}
if (isString(item)) { if (isString(item)) {
stream.push(item) stream.push(item)
} else { } else {
await unrollBuffer(item, stream) // since this is a sync buffer, child buffers are never promises
unrollBufferSync(item as SSRBuffer, stream)
} }
} }
} }

View File

@ -12,16 +12,36 @@ import { SSRContext, renderComponentVNode, SSRBuffer } from './render'
const { isVNode } = ssrUtils const { isVNode } = ssrUtils
async function unrollBuffer(buffer: SSRBuffer): Promise<string> { async function unrollBuffer(buffer: SSRBuffer): Promise<string> {
if (buffer.hasAsync) {
let ret = ''
for (let i = 0; i < buffer.length; i++) {
let item = buffer[i]
if (isPromise(item)) {
item = await item
}
if (isString(item)) {
ret += item
} else {
ret += await unrollBuffer(item)
}
}
return ret
} else {
// sync buffer can be more efficiently unrolled without unnecessary await
// ticks
return unrollBufferSync(buffer)
}
}
function unrollBufferSync(buffer: SSRBuffer): string {
let ret = '' let ret = ''
for (let i = 0; i < buffer.length; i++) { for (let i = 0; i < buffer.length; i++) {
let item = buffer[i] let item = buffer[i]
if (isPromise(item)) {
item = await item
}
if (isString(item)) { if (isString(item)) {
ret += item ret += item
} else { } else {
ret += await unrollBuffer(item as SSRBuffer) // since this is a sync buffer, child buffers are never promises
ret += unrollBufferSync(item as SSRBuffer)
} }
} }
return ret return ret