Skip to content

feat(ssr-render): suspense supports server-side triggering of onResolve #8555

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 10 commits into
base: main
Choose a base branch
from
19 changes: 18 additions & 1 deletion packages/runtime-core/src/components/Suspense.ts
Original file line number Diff line number Diff line change
Expand Up @@ -614,7 +614,6 @@
}
}
}

// invoke @resolve event
triggerEvent(vnode, 'onResolve')
},
Expand Down Expand Up @@ -902,3 +901,21 @@
const suspensible = vnode.props && vnode.props.suspensible
return suspensible != null && suspensible !== false
}

export type ssrSuspenseBoundary = {
deps: number
resolve: (node: VNode) => void
vnode: VNode
parentSuspense: null | ssrSuspenseBoundary
} & SuspenseBoundary
export function createSSRSuspenseBoundary(vnode: VNode) {
return {
deps: 0,
resolve(vnode: VNode) {

Check failure on line 914 in packages/runtime-core/src/components/Suspense.ts

View workflow job for this annotation

GitHub Actions / continuous-release

Method must have an explicit return type annotation with --isolatedDeclarations.

Check failure on line 914 in packages/runtime-core/src/components/Suspense.ts

View workflow job for this annotation

GitHub Actions / test / lint-and-test-dts

Method must have an explicit return type annotation with --isolatedDeclarations.
// invoke @resolve event
triggerEvent(vnode, 'onResolve')
},
vnode: vnode,

Check failure on line 918 in packages/runtime-core/src/components/Suspense.ts

View workflow job for this annotation

GitHub Actions / continuous-release

Expression type can't be inferred with --isolatedDeclarations.

Check failure on line 918 in packages/runtime-core/src/components/Suspense.ts

View workflow job for this annotation

GitHub Actions / test / lint-and-test-dts

Expression type can't be inferred with --isolatedDeclarations.
parentSuspense: null,
}
}
7 changes: 6 additions & 1 deletion packages/runtime-core/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -113,7 +113,12 @@ export { createVNode, cloneVNode, mergeProps, isVNode } from './vnode'
export { Fragment, Text, Comment, Static, type VNodeRef } from './vnode'
// Built-in components
export { Teleport, type TeleportProps } from './components/Teleport'
export { Suspense, type SuspenseProps } from './components/Suspense'
export {
Suspense,
createSSRSuspenseBoundary,
type ssrSuspenseBoundary,
type SuspenseProps,
} from './components/Suspense'
export { KeepAlive, type KeepAliveProps } from './components/KeepAlive'
export {
BaseTransition,
Expand Down
1 change: 1 addition & 0 deletions packages/server-renderer/__tests__/render.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -963,6 +963,7 @@ function testRender(type: string, render: typeof renderToString) {
_push,
createVNode(resolveDynamicComponent('B'), null, null),
_parent,
null,
)
},
}),
Expand Down
107 changes: 107 additions & 0 deletions packages/server-renderer/__tests__/ssrSuspense.spec.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { Suspense, createApp, h } from 'vue'
import { renderToString } from '../src/renderToString'
import { expect } from 'vitest'

describe('SSR Suspense', () => {
const ResolvingAsync = {
Expand All @@ -8,6 +9,12 @@ describe('SSR Suspense', () => {
},
}

const ResolvingSync = {
setup() {
return () => h('div', 'sync')
},
}

const RejectingAsync = {
setup() {
return new Promise((_, reject) => reject('foo'))
Expand Down Expand Up @@ -78,6 +85,106 @@ describe('SSR Suspense', () => {
expect('missing template or render function').toHaveBeenWarned()
})

test('suspense onResolve & ssr render & async', async () => {
const onResolve = vi.fn()
const Comp = {
render() {
return h(
Suspense,
{
onResolve,
},
{
default: h('div', [h(ResolvingAsync), h(ResolvingAsync)]),
fallback: h('div', 'fallback'),
},
)
},
}
expect(await renderToString(createApp(Comp))).toBe(
`<div><div>async</div><div>async</div></div>`,
)
expect(onResolve).toHaveBeenCalledTimes(1)
})

test('suspense onResolve & ssr render & nested async deps', async () => {
const onResolve = vi.fn()
const child = {
async setup() {
return () => h(ResolvingAsync)
},
}
const Comp = {
render() {
return h(
Suspense,
{
onResolve,
},
{
default: h('div', [h(child)]),
fallback: h('div', 'fallback'),
},
)
},
}
expect(await renderToString(createApp(Comp))).toBe(
`<div><div>async</div></div>`,
)
expect(onResolve).toHaveBeenCalledTimes(1)
})

test('suspense onResolve & ssr render & sync', async () => {
const onResolve = vi.fn()
const Comp = {
render() {
return h(
Suspense,
{
onResolve,
},
{
default: h('div', [h(ResolvingSync), h(ResolvingSync)]),
fallback: h('div', 'fallback'),
},
)
},
}
expect(await renderToString(createApp(Comp))).toBe(
`<div><div>sync</div><div>sync</div></div>`,
)
expect(onResolve).toHaveBeenCalledTimes(1)
})

test('nested suspense onResolve & ssr render', async () => {
const onResolve = vi.fn()
const onNestedResolve = vi.fn()
const Comp = {
render() {
return h(
Suspense,
{
onResolve,
},
{
default: h(
Suspense,
{
onResolve: onNestedResolve,
},
{
default: h(ResolvingAsync),
},
),
},
)
},
}
expect(await renderToString(createApp(Comp))).toBe(`<div>async</div>`)
expect(onResolve).toHaveBeenCalledTimes(1)
expect(onNestedResolve).toHaveBeenCalledTimes(1)
})

test('failing suspense in passing suspense', async () => {
const Comp = {
errorCaptured: vi.fn(() => false),
Expand Down
1 change: 1 addition & 0 deletions packages/server-renderer/src/helpers/ssrRenderComponent.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ export function ssrRenderComponent(
return renderComponentVNode(
createVNode(comp, props, children),
parentComponent,
null,
slotScopeId,
)
}
1 change: 1 addition & 0 deletions packages/server-renderer/src/helpers/ssrRenderSlot.ts
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,7 @@ export function ssrRenderSlotInner(
push,
validSlotContent,
parentComponent,
null,
slotScopeId,
)
} else if (fallbackRenderFn) {
Expand Down
Loading
Loading