]> git.ipfire.org Git - thirdparty/vuejs/core.git/commitdiff
perf(ssr): avoid unnecessary async overhead
authorEvan You <yyx990803@gmail.com>
Sun, 26 Jan 2020 21:13:12 +0000 (16:13 -0500)
committerEvan You <yyx990803@gmail.com>
Mon, 27 Jan 2020 21:00:17 +0000 (16:00 -0500)
packages/runtime-core/src/component.ts
packages/server-renderer/src/index.ts

index 8117d68d7c6d9791422226adb86f30cbcc87f798..ff3533bd55fbfec7b58aef60552383198ed9cf73 100644 (file)
@@ -337,7 +337,9 @@ function setupStatefulComponent(
     if (isPromise(setupResult)) {
       if (__SSR__) {
         // return the promise so server-renderer can wait on it
-        return setupResult
+        return setupResult.then(resolvedResult => {
+          handleSetupResult(instance, resolvedResult, parentSuspense)
+        })
       } else if (__FEATURE_SUSPENSE__) {
         // async setup returned Promise.
         // bail here and wait for re-entry.
index e78099a6703065347304c6df468b83b3f3308081..19034429f6f732fe5e7fa41ed84910b3fd4a8fd3 100644 (file)
@@ -7,17 +7,21 @@ import {
   VNode,
   createVNode
 } from 'vue'
-import { isString } from '@vue/shared'
+import { isString, isPromise, isArray } from '@vue/shared'
 
 type SSRBuffer = SSRBufferItem[]
-type SSRBufferItem = string | Promise<SSRBuffer>
+type SSRBufferItem = string | ResolvedSSRBuffer | Promise<SSRBuffer>
 type ResolvedSSRBuffer = (string | ResolvedSSRBuffer)[]
 
-function createSSRBuffer() {
+function createBuffer() {
   let appendable = false
+  let hasAsync = false
   const buffer: SSRBuffer = []
   return {
     buffer,
+    hasAsync() {
+      return hasAsync
+    },
     push(item: SSRBufferItem) {
       const isStringItem = isString(item)
       if (appendable && isStringItem) {
@@ -26,18 +30,14 @@ function createSSRBuffer() {
         buffer.push(item)
       }
       appendable = isStringItem
+      if (!isStringItem && !isArray(item)) {
+        // promise
+        hasAsync = true
+      }
     }
   }
 }
 
-export async function renderToString(app: App): Promise<string> {
-  const resolvedBuffer = (await renderComponent(
-    app._component,
-    app._props
-  )) as ResolvedSSRBuffer
-  return unrollBuffer(resolvedBuffer)
-}
-
 function unrollBuffer(buffer: ResolvedSSRBuffer): string {
   let ret = ''
   for (let i = 0; i < buffer.length; i++) {
@@ -51,20 +51,35 @@ function unrollBuffer(buffer: ResolvedSSRBuffer): string {
   return ret
 }
 
-export async function renderComponent(
+export async function renderToString(app: App): Promise<string> {
+  const resolvedBuffer = (await renderComponent(
+    app._component,
+    app._props
+  )) as ResolvedSSRBuffer
+  return unrollBuffer(resolvedBuffer)
+}
+
+export function renderComponent(
   comp: Component,
   props: Record<string, any> | null = null,
   children: VNode['children'] = null,
   parentComponent: ComponentInternalInstance | null = null
-): Promise<SSRBuffer> {
-  // 1. create component buffer
-  const { buffer, push } = createSSRBuffer()
-
-  // 2. create actual instance
+): ResolvedSSRBuffer | Promise<SSRBuffer> {
   const vnode = createVNode(comp, props, children)
   const instance = createComponentInstance(vnode, parentComponent)
-  await setupComponent(instance, null)
+  const res = setupComponent(instance, null)
+  if (isPromise(res)) {
+    return res.then(() => innerRenderComponent(comp, instance))
+  } else {
+    return innerRenderComponent(comp, instance)
+  }
+}
 
+function innerRenderComponent(
+  comp: Component,
+  instance: ComponentInternalInstance
+): ResolvedSSRBuffer | Promise<SSRBuffer> {
+  const { buffer, push, hasAsync } = createBuffer()
   if (typeof comp === 'function') {
     // TODO FunctionalComponent
   } else {
@@ -77,7 +92,11 @@ export async function renderComponent(
       // TODO warn component missing render function
     }
   }
-  // TS can't figure this out due to recursive occurance of Promise in type
-  // @ts-ignore
-  return Promise.all(buffer)
+  // 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
+  // contains only sync children so we can avoid the async book-keeping overhead.
+  return hasAsync()
+    ? // TS can't figure out the typing due to recursive appearance of Promise
+      Promise.all(buffer as any)
+    : (buffer as ResolvedSSRBuffer)
 }