]> git.ipfire.org Git - thirdparty/vuejs/core.git/commitdiff
perf(ssr): avoid unnecessary await ticks when unrolling sync buffers
authorEvan You <yyx990803@gmail.com>
Fri, 26 Jun 2020 15:10:30 +0000 (11:10 -0400)
committerEvan You <yyx990803@gmail.com>
Fri, 26 Jun 2020 15:10:30 +0000 (11:10 -0400)
packages/server-renderer/src/render.ts
packages/server-renderer/src/renderToStream.ts
packages/server-renderer/src/renderToString.ts

index 15f052235b8a9db0f94b601bafad36b560ed23f3..9f166aa6edf7ee128c7ba37a1cb4cdd02491fa22 100644 (file)
@@ -20,7 +20,8 @@ import {
   isPromise,
   isString,
   isVoidTag,
-  ShapeFlags
+  ShapeFlags,
+  isArray
 } from '@vue/shared'
 import { ssrRenderAttrs } from './helpers/ssrRenderAttrs'
 import { ssrCompile } from './helpers/ssrCompile'
@@ -35,7 +36,7 @@ const {
   normalizeSuspenseChildren
 } = ssrUtils
 
-export type SSRBuffer = SSRBufferItem[]
+export type SSRBuffer = SSRBufferItem[] & { hasAsync?: boolean }
 export type SSRBufferItem = string | SSRBuffer | Promise<SSRBuffer>
 export type PushFn = (item: SSRBufferItem) => void
 export type Props = Record<string, unknown>
@@ -68,6 +69,11 @@ export function createBuffer() {
         buffer.push(item)
       }
       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
+      }
     }
   }
 }
index 63b389522542b5260cc2471cd293381634bc7e09..4952b51c2676bb7e47a88d5a2466c215f2b0f371 100644 (file)
@@ -16,15 +16,33 @@ async function unrollBuffer(
   buffer: SSRBuffer,
   stream: Readable
 ): 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++) {
     let item = buffer[i]
-    if (isPromise(item)) {
-      item = await item
-    }
     if (isString(item)) {
       stream.push(item)
     } else {
-      await unrollBuffer(item, stream)
+      // since this is a sync buffer, child buffers are never promises
+      unrollBufferSync(item as SSRBuffer, stream)
     }
   }
 }
index 68051da82e835133b62932f2091f3ef4781496b5..7e696a6e7f8bf75f3b94dc9d13a14be73f438f63 100644 (file)
@@ -12,16 +12,36 @@ import { SSRContext, renderComponentVNode, SSRBuffer } from './render'
 const { isVNode } = ssrUtils
 
 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 = ''
   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 as SSRBuffer)
+      // since this is a sync buffer, child buffers are never promises
+      ret += unrollBufferSync(item as SSRBuffer)
     }
   }
   return ret