Skip to content

Commit c73b4a0

Browse files
committed
feat(ssr): serverPrefetch
1 parent 63f1f18 commit c73b4a0

File tree

4 files changed

+55
-5
lines changed

4 files changed

+55
-5
lines changed

packages/runtime-core/src/componentOptions.ts

+1
Original file line numberDiff line numberDiff line change
@@ -103,6 +103,7 @@ export interface ComponentOptionsBase<
103103
directives?: Record<string, Directive>
104104
inheritAttrs?: boolean
105105
emits?: (E | EE[]) & ThisType<void>
106+
serverPrefetch?(): Promise<any>
106107

107108
// Internal ------------------------------------------------------------------
108109

packages/server-renderer/__tests__/renderToStream.spec.ts

+20
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ import { renderToStream as _renderToStream } from '../src/renderToStream'
1515
import { Readable } from 'stream'
1616
import { ssrRenderSlot } from '../src/helpers/ssrRenderSlot'
1717
import { ssrRenderComponent } from '../src/helpers/ssrRenderComponent'
18+
1819
const promisifyStream = (stream: Readable) => {
1920
return new Promise((resolve, reject) => {
2021
let result = ''
@@ -599,4 +600,23 @@ describe('ssr: renderToStream', () => {
599600
)
600601
})
601602
})
603+
604+
test('serverPrefetch', async () => {
605+
const msg = Promise.resolve('hello')
606+
const app = createApp({
607+
data() {
608+
return {
609+
msg: ''
610+
}
611+
},
612+
async serverPrefetch() {
613+
this.msg = await msg
614+
},
615+
render() {
616+
return h('div', this.msg)
617+
}
618+
})
619+
const html = await renderToStream(app)
620+
expect(html).toBe(`<div>hello</div>`)
621+
})
602622
})

packages/server-renderer/__tests__/renderToString.spec.ts

+20
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ import { escapeHtml } from '@vue/shared'
1414
import { renderToString } from '../src/renderToString'
1515
import { ssrRenderSlot, SSRSlot } from '../src/helpers/ssrRenderSlot'
1616
import { ssrRenderComponent } from '../src/helpers/ssrRenderComponent'
17+
1718
describe('ssr: renderToString', () => {
1819
test('should apply app context', async () => {
1920
const app = createApp({
@@ -580,4 +581,23 @@ describe('ssr: renderToString', () => {
580581
).toHaveBeenWarned()
581582
})
582583
})
584+
585+
test('serverPrefetch', async () => {
586+
const msg = Promise.resolve('hello')
587+
const app = createApp({
588+
data() {
589+
return {
590+
msg: ''
591+
}
592+
},
593+
async serverPrefetch() {
594+
this.msg = await msg
595+
},
596+
render() {
597+
return h('div', this.msg)
598+
}
599+
})
600+
const html = await renderToString(app)
601+
expect(html).toBe(`<div>hello</div>`)
602+
})
583603
})

packages/server-renderer/src/render.ts

+14-5
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import {
22
Comment,
33
Component,
44
ComponentInternalInstance,
5+
ComponentOptions,
56
DirectiveBinding,
67
Fragment,
78
mergeProps,
@@ -84,12 +85,20 @@ export function renderComponentVNode(
8485
): SSRBuffer | Promise<SSRBuffer> {
8586
const instance = createComponentInstance(vnode, parentComponent, null)
8687
const res = setupComponent(instance, true /* isSSR */)
87-
if (isPromise(res)) {
88-
return res
89-
.catch(err => {
90-
warn(`[@vue/server-renderer]: Uncaught error in async setup:\n`, err)
88+
const hasAsyncSetup = isPromise(res)
89+
const prefetch = (vnode.type as ComponentOptions).serverPrefetch
90+
if (hasAsyncSetup || prefetch) {
91+
let p = hasAsyncSetup
92+
? (res as Promise<void>).catch(err => {
93+
warn(`[@vue/server-renderer]: Uncaught error in async setup:\n`, err)
94+
})
95+
: Promise.resolve()
96+
if (prefetch) {
97+
p = p.then(() => prefetch.call(instance.proxy)).catch(err => {
98+
warn(`[@vue/server-renderer]: Uncaught error in serverPrefetch:\n`, err)
9199
})
92-
.then(() => renderComponentSubTree(instance))
100+
}
101+
return p.then(() => renderComponentSubTree(instance))
93102
} else {
94103
return renderComponentSubTree(instance)
95104
}

0 commit comments

Comments
 (0)