feat(ssr): serverPrefetch

This commit is contained in:
Evan You 2020-09-01 22:52:46 -04:00
parent 63f1f18064
commit c73b4a0e10
4 changed files with 55 additions and 5 deletions

View File

@ -103,6 +103,7 @@ export interface ComponentOptionsBase<
directives?: Record<string, Directive> directives?: Record<string, Directive>
inheritAttrs?: boolean inheritAttrs?: boolean
emits?: (E | EE[]) & ThisType<void> emits?: (E | EE[]) & ThisType<void>
serverPrefetch?(): Promise<any>
// Internal ------------------------------------------------------------------ // Internal ------------------------------------------------------------------

View File

@ -15,6 +15,7 @@ import { renderToStream as _renderToStream } from '../src/renderToStream'
import { Readable } from 'stream' import { Readable } from 'stream'
import { ssrRenderSlot } from '../src/helpers/ssrRenderSlot' import { ssrRenderSlot } from '../src/helpers/ssrRenderSlot'
import { ssrRenderComponent } from '../src/helpers/ssrRenderComponent' import { ssrRenderComponent } from '../src/helpers/ssrRenderComponent'
const promisifyStream = (stream: Readable) => { const promisifyStream = (stream: Readable) => {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
let result = '' 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>`)
})
}) })

View File

@ -14,6 +14,7 @@ import { escapeHtml } from '@vue/shared'
import { renderToString } from '../src/renderToString' import { renderToString } from '../src/renderToString'
import { ssrRenderSlot, SSRSlot } from '../src/helpers/ssrRenderSlot' import { ssrRenderSlot, SSRSlot } from '../src/helpers/ssrRenderSlot'
import { ssrRenderComponent } from '../src/helpers/ssrRenderComponent' import { ssrRenderComponent } from '../src/helpers/ssrRenderComponent'
describe('ssr: renderToString', () => { describe('ssr: renderToString', () => {
test('should apply app context', async () => { test('should apply app context', async () => {
const app = createApp({ const app = createApp({
@ -580,4 +581,23 @@ describe('ssr: renderToString', () => {
).toHaveBeenWarned() ).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>`)
})
}) })

View File

@ -2,6 +2,7 @@ import {
Comment, Comment,
Component, Component,
ComponentInternalInstance, ComponentInternalInstance,
ComponentOptions,
DirectiveBinding, DirectiveBinding,
Fragment, Fragment,
mergeProps, mergeProps,
@ -84,12 +85,20 @@ export function renderComponentVNode(
): SSRBuffer | Promise<SSRBuffer> { ): SSRBuffer | Promise<SSRBuffer> {
const instance = createComponentInstance(vnode, parentComponent, null) const instance = createComponentInstance(vnode, parentComponent, null)
const res = setupComponent(instance, true /* isSSR */) const res = setupComponent(instance, true /* isSSR */)
if (isPromise(res)) { const hasAsyncSetup = isPromise(res)
return res const prefetch = (vnode.type as ComponentOptions).serverPrefetch
.catch(err => { if (hasAsyncSetup || prefetch) {
warn(`[@vue/server-renderer]: Uncaught error in async setup:\n`, err) 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 { } else {
return renderComponentSubTree(instance) return renderComponentSubTree(instance)
} }