refactor(ssr): extract buffer resolving and resolvePortals (#771)

This commit is contained in:
Xin Du (Clark) 2020-02-24 16:23:35 +00:00 committed by GitHub
parent 20afd4093d
commit 047844cfb8
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

View File

@ -67,9 +67,11 @@ function createBuffer() {
let hasAsync = false let hasAsync = false
const buffer: SSRBuffer = [] const buffer: SSRBuffer = []
return { return {
buffer, getBuffer(): ResolvedSSRBuffer | Promise<ResolvedSSRBuffer> {
hasAsync() { // If the current component's buffer contains any Promise from async children,
return hasAsync // then it must return a Promise too. Otherwise this is a component that
// contains only sync children so we can avoid the async book-keeping overhead.
return hasAsync ? Promise.all(buffer) : (buffer as ResolvedSSRBuffer)
}, },
push(item: SSRBufferItem) { push(item: SSRBufferItem) {
const isStringItem = isString(item) const isStringItem = isString(item)
@ -104,28 +106,19 @@ export async function renderToString(
input: App | VNode, input: App | VNode,
context: SSRContext = {} context: SSRContext = {}
): Promise<string> { ): Promise<string> {
let buffer: ResolvedSSRBuffer
if (isVNode(input)) { if (isVNode(input)) {
// raw vnode, wrap with app (for context) // raw vnode, wrap with app (for context)
return renderToString(createApp({ render: () => input }), context) return renderToString(createApp({ render: () => input }), context)
} else {
// rendering an app
const vnode = createVNode(input._component, input._props)
vnode.appContext = input._context
// provide the ssr context to the tree
input.provide(ssrContextKey, context)
buffer = await renderComponentVNode(vnode)
} }
// resolve portals // rendering an app
if (context.__portalBuffers) { const vnode = createVNode(input._component, input._props)
context.portals = context.portals || {} vnode.appContext = input._context
for (const key in context.__portalBuffers) { // provide the ssr context to the tree
// note: it's OK to await sequentially here because the Promises were input.provide(ssrContextKey, context)
// created eagerly in parallel. const buffer = await renderComponentVNode(vnode)
context.portals[key] = unrollBuffer(await context.__portalBuffers[key])
} await resolvePortals(context)
}
return unrollBuffer(buffer) return unrollBuffer(buffer)
} }
@ -201,7 +194,7 @@ function renderComponentSubTree(
instance: ComponentInternalInstance instance: ComponentInternalInstance
): ResolvedSSRBuffer | Promise<ResolvedSSRBuffer> { ): ResolvedSSRBuffer | Promise<ResolvedSSRBuffer> {
const comp = instance.type as Component const comp = instance.type as Component
const { buffer, push, hasAsync } = createBuffer() const { getBuffer, push } = createBuffer()
if (isFunction(comp)) { if (isFunction(comp)) {
renderVNode(push, renderComponentRoot(instance), instance) renderVNode(push, renderComponentRoot(instance), instance)
} else { } else {
@ -225,10 +218,7 @@ function renderComponentSubTree(
) )
} }
} }
// If the current component's buffer contains any Promise from async children, return getBuffer()
// then it must return a Promise too. Otherwise this is a component that
// contains only sync children so we can avoid the async book-keeping overhead.
return hasAsync() ? Promise.all(buffer) : (buffer as ResolvedSSRBuffer)
} }
function renderVNode( function renderVNode(
@ -349,7 +339,7 @@ function renderPortal(
return [] return []
} }
const { buffer, push, hasAsync } = createBuffer() const { getBuffer, push } = createBuffer()
renderVNodeChildren( renderVNodeChildren(
push, push,
vnode.children as VNodeArrayChildren, vnode.children as VNodeArrayChildren,
@ -360,7 +350,17 @@ function renderPortal(
] as SSRContext ] as SSRContext
const portalBuffers = const portalBuffers =
context.__portalBuffers || (context.__portalBuffers = {}) context.__portalBuffers || (context.__portalBuffers = {})
portalBuffers[target] = hasAsync()
? Promise.all(buffer) portalBuffers[target] = getBuffer()
: (buffer as ResolvedSSRBuffer) }
async function resolvePortals(context: SSRContext) {
if (context.__portalBuffers) {
context.portals = context.portals || {}
for (const key in context.__portalBuffers) {
// note: it's OK to await sequentially here because the Promises were
// created eagerly in parallel.
context.portals[key] = unrollBuffer(await context.__portalBuffers[key])
}
}
} }