]> git.ipfire.org Git - thirdparty/vuejs/core.git/commitdiff
feat(ssr): serverPrefetch
authorEvan You <yyx990803@gmail.com>
Wed, 2 Sep 2020 02:52:46 +0000 (22:52 -0400)
committerEvan You <yyx990803@gmail.com>
Wed, 2 Sep 2020 02:52:46 +0000 (22:52 -0400)
packages/runtime-core/src/componentOptions.ts
packages/server-renderer/__tests__/renderToStream.spec.ts
packages/server-renderer/__tests__/renderToString.spec.ts
packages/server-renderer/src/render.ts

index ae7e80a40c2c1b0850f9f37908302757962a8a57..5af8c410dd6d85213b25194ca01321d0eea5b5b9 100644 (file)
@@ -103,6 +103,7 @@ export interface ComponentOptionsBase<
   directives?: Record<string, Directive>
   inheritAttrs?: boolean
   emits?: (E | EE[]) & ThisType<void>
+  serverPrefetch?(): Promise<any>
 
   // Internal ------------------------------------------------------------------
 
index 4c9466b6725515ea8e02892e0d37d5cc0331fc05..410be382e5fe21275743146eef4631f3dd1326b9 100644 (file)
@@ -15,6 +15,7 @@ import { renderToStream as _renderToStream } from '../src/renderToStream'
 import { Readable } from 'stream'
 import { ssrRenderSlot } from '../src/helpers/ssrRenderSlot'
 import { ssrRenderComponent } from '../src/helpers/ssrRenderComponent'
+
 const promisifyStream = (stream: Readable) => {
   return new Promise((resolve, reject) => {
     let result = ''
@@ -599,4 +600,23 @@ describe('ssr: renderToStream', () => {
       )
     })
   })
+
+  test('serverPrefetch', async () => {
+    const msg = Promise.resolve('hello')
+    const app = createApp({
+      data() {
+        return {
+          msg: ''
+        }
+      },
+      async serverPrefetch() {
+        this.msg = await msg
+      },
+      render() {
+        return h('div', this.msg)
+      }
+    })
+    const html = await renderToStream(app)
+    expect(html).toBe(`<div>hello</div>`)
+  })
 })
index dfaab64c2c60b0d98279637a6113f1ce623c2031..25704eeee4668a2fa94f9786cdc80e3f7647ef1e 100644 (file)
@@ -14,6 +14,7 @@ import { escapeHtml } from '@vue/shared'
 import { renderToString } from '../src/renderToString'
 import { ssrRenderSlot, SSRSlot } from '../src/helpers/ssrRenderSlot'
 import { ssrRenderComponent } from '../src/helpers/ssrRenderComponent'
+
 describe('ssr: renderToString', () => {
   test('should apply app context', async () => {
     const app = createApp({
@@ -580,4 +581,23 @@ describe('ssr: renderToString', () => {
       ).toHaveBeenWarned()
     })
   })
+
+  test('serverPrefetch', async () => {
+    const msg = Promise.resolve('hello')
+    const app = createApp({
+      data() {
+        return {
+          msg: ''
+        }
+      },
+      async serverPrefetch() {
+        this.msg = await msg
+      },
+      render() {
+        return h('div', this.msg)
+      }
+    })
+    const html = await renderToString(app)
+    expect(html).toBe(`<div>hello</div>`)
+  })
 })
index 0cb20625b099cbc235e0329fff19c680de9132e9..44ec2be14fe790934163554c74ca47bbb10cf335 100644 (file)
@@ -2,6 +2,7 @@ import {
   Comment,
   Component,
   ComponentInternalInstance,
+  ComponentOptions,
   DirectiveBinding,
   Fragment,
   mergeProps,
@@ -84,12 +85,20 @@ export function renderComponentVNode(
 ): SSRBuffer | Promise<SSRBuffer> {
   const instance = createComponentInstance(vnode, parentComponent, null)
   const res = setupComponent(instance, true /* isSSR */)
-  if (isPromise(res)) {
-    return res
-      .catch(err => {
-        warn(`[@vue/server-renderer]: Uncaught error in async setup:\n`, err)
+  const hasAsyncSetup = isPromise(res)
+  const prefetch = (vnode.type as ComponentOptions).serverPrefetch
+  if (hasAsyncSetup || prefetch) {
+    let p = hasAsyncSetup
+      ? (res as Promise<void>).catch(err => {
+          warn(`[@vue/server-renderer]: Uncaught error in async setup:\n`, err)
+        })
+      : Promise.resolve()
+    if (prefetch) {
+      p = p.then(() => prefetch.call(instance.proxy)).catch(err => {
+        warn(`[@vue/server-renderer]: Uncaught error in serverPrefetch:\n`, err)
       })
-      .then(() => renderComponentSubTree(instance))
+    }
+    return p.then(() => renderComponentSubTree(instance))
   } else {
     return renderComponentSubTree(instance)
   }