wip(ssr): improve buffer typing

This commit is contained in:
Evan You 2020-01-28 10:46:13 -05:00
parent 78beed2574
commit 8857b4f288

View File

@ -10,8 +10,14 @@ import {
} from 'vue' } from 'vue'
import { isString, isPromise, isArray, isFunction } from '@vue/shared' import { isString, isPromise, isArray, isFunction } from '@vue/shared'
// Each component has a buffer array.
// A buffer array can contain one of the following:
// - plain string
// - A resolved buffer (recursive arrays of strings that can be unrolled
// synchronously)
// - An async buffer (a Promise that resolves to a resolved buffer)
type SSRBuffer = SSRBufferItem[] type SSRBuffer = SSRBufferItem[]
type SSRBufferItem = string | ResolvedSSRBuffer | Promise<SSRBuffer> type SSRBufferItem = string | ResolvedSSRBuffer | Promise<ResolvedSSRBuffer>
type ResolvedSSRBuffer = (string | ResolvedSSRBuffer)[] type ResolvedSSRBuffer = (string | ResolvedSSRBuffer)[]
function createBuffer() { function createBuffer() {
@ -53,10 +59,7 @@ function unrollBuffer(buffer: ResolvedSSRBuffer): string {
} }
export async function renderToString(app: App): Promise<string> { export async function renderToString(app: App): Promise<string> {
const resolvedBuffer = (await renderComponent( const resolvedBuffer = await renderComponent(app._component, app._props)
app._component,
app._props
)) as ResolvedSSRBuffer
return unrollBuffer(resolvedBuffer) return unrollBuffer(resolvedBuffer)
} }
@ -65,7 +68,7 @@ export function renderComponent(
props: Record<string, any> | null = null, props: Record<string, any> | null = null,
children: VNode['children'] = null, children: VNode['children'] = null,
parentComponent: ComponentInternalInstance | null = null parentComponent: ComponentInternalInstance | null = null
): ResolvedSSRBuffer | Promise<SSRBuffer> { ): ResolvedSSRBuffer | Promise<ResolvedSSRBuffer> {
const vnode = createVNode(comp, props, children) const vnode = createVNode(comp, props, children)
const instance = createComponentInstance(vnode, parentComponent) const instance = createComponentInstance(vnode, parentComponent)
const res = setupComponent(instance, null) const res = setupComponent(instance, null)
@ -79,7 +82,7 @@ export function renderComponent(
function innerRenderComponent( function innerRenderComponent(
comp: Component, comp: Component,
instance: ComponentInternalInstance instance: ComponentInternalInstance
): ResolvedSSRBuffer | Promise<SSRBuffer> { ): ResolvedSSRBuffer | Promise<ResolvedSSRBuffer> {
const { buffer, push, hasAsync } = createBuffer() const { buffer, push, hasAsync } = createBuffer()
if (isFunction(comp)) { if (isFunction(comp)) {
renderVNode(push, renderComponentRoot(instance)) renderVNode(push, renderComponentRoot(instance))
@ -101,10 +104,7 @@ function innerRenderComponent(
// If the current component's buffer contains any Promise from async children, // If the current component's buffer contains any Promise from async children,
// then it must return a Promise too. Otherwise this is a component that // 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. // contains only sync children so we can avoid the async book-keeping overhead.
return hasAsync() return hasAsync() ? Promise.all(buffer) : (buffer as ResolvedSSRBuffer)
? // TS can't figure out the typing due to recursive appearance of Promise
Promise.all(buffer as any)
: (buffer as ResolvedSSRBuffer)
} }
export function renderVNode(push: (item: SSRBufferItem) => void, vnode: VNode) { export function renderVNode(push: (item: SSRBufferItem) => void, vnode: VNode) {