Skip to content

Commit 90336e9

Browse files
committed
feat: support async setup (fix #463)
1 parent 3d9e1f2 commit 90336e9

File tree

4 files changed

+74
-6
lines changed

4 files changed

+74
-6
lines changed

src/helpers.ts

+5-3
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { ComponentOptions, ShallowUnwrapRef, Ref } from 'vue'
22

3-
import { Vue, VueConstructor, VueMixin } from './vue'
3+
import { Vue, VueBase, VueConstructor, VueMixin } from './vue'
44

55
export function Options<V extends Vue>(
66
options: ComponentOptions & ThisType<V>
@@ -80,10 +80,12 @@ export type UnwrapSetupValue<T> = T extends Ref<infer R>
8080
? R
8181
: ShallowUnwrapRef<T>
8282

83-
export function setup<R>(setupFn: () => R): UnwrapSetupValue<R> {
83+
export type UnwrapPromise<T> = T extends Promise<infer R> ? R : T
84+
85+
export function setup<R>(setupFn: () => R): UnwrapSetupValue<UnwrapPromise<R>> {
8486
// Hack to delay the invocation of setup function.
8587
// Will be called after dealing with class properties.
8688
return {
8789
__s: setupFn,
88-
} as UnwrapSetupValue<R>
90+
} as UnwrapSetupValue<UnwrapPromise<R>>
8991
}

src/index.ts

+1
Original file line numberDiff line numberDiff line change
@@ -38,4 +38,5 @@ export {
3838
UnionToIntersection,
3939
ExtractInstance,
4040
UnwrapSetupValue,
41+
UnwrapPromise,
4142
} from './helpers'

src/vue.ts

+15-2
Original file line numberDiff line numberDiff line change
@@ -243,6 +243,7 @@ class VueImpl {
243243
const dataKeys = Object.keys(data)
244244

245245
const plainData: any = {}
246+
let promise: Promise<any> | null = null
246247

247248
// Initialize reactive data and convert constructor `this` to a proxy
248249
dataKeys.forEach((key) => {
@@ -260,11 +261,23 @@ class VueImpl {
260261
dataKeys.forEach((key) => {
261262
if (data[key] && data[key].__s) {
262263
const setupState = data[key].__s()
263-
plainData[key] = proxyRefs(setupState)
264+
if (setupState instanceof Promise) {
265+
if (!promise) {
266+
promise = Promise.resolve(plainData)
267+
}
268+
promise = promise.then(() => {
269+
return setupState.then((value) => {
270+
plainData[key] = proxyRefs(value)
271+
return plainData
272+
})
273+
})
274+
} else {
275+
plainData[key] = proxyRefs(setupState)
276+
}
264277
}
265278
})
266279

267-
return plainData
280+
return promise ?? plainData
268281
}
269282

270283
const decorators =

test/specs/test.spec.ts

+53-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,15 @@
11
import 'reflect-metadata'
2-
import { h, resolveComponent, ref, onMounted, Ref, watch, toRef } from 'vue'
2+
import {
3+
h,
4+
resolveComponent,
5+
ref,
6+
onMounted,
7+
Ref,
8+
watch,
9+
toRef,
10+
nextTick,
11+
Suspense,
12+
} from 'vue'
313
import { Options, createDecorator, mixins, Vue, setup, prop } from '../../src'
414
import { mount, unmount } from '../helpers'
515

@@ -416,6 +426,48 @@ describe('vue-class-component', () => {
416426
expect(root.answer.nested.answer.value).toBe(42)
417427
})
418428

429+
it('setup: suspense', () => {
430+
const deps: Promise<any>[] = []
431+
432+
class Child extends Vue {
433+
foo = 'Hello'
434+
435+
bar = setup(() => {
436+
const a = ref(42)
437+
return {
438+
a,
439+
}
440+
})
441+
442+
baz = setup(() => {
443+
const b = ref(true)
444+
const p = Promise.resolve({
445+
b,
446+
})
447+
deps.push(p.then(() => Promise.resolve()))
448+
return p
449+
})
450+
451+
render() {
452+
return h('div', [[this.foo, this.bar.a, this.baz.b].join(',')])
453+
}
454+
}
455+
456+
class App extends Vue {
457+
render() {
458+
return h(Suspense, null, {
459+
default: h(Child),
460+
fallback: h('div', 'fallback'),
461+
})
462+
}
463+
}
464+
465+
const { root } = mount(App)
466+
return Promise.all(deps)
467+
.then(() => nextTick())
468+
.then(() => expect(root.$el.textContent).toBe('Hello,42,true'))
469+
})
470+
419471
it('reactive class properties in a composition function', (done) => {
420472
function test(message: Ref<string>) {
421473
watch(message, () => {

0 commit comments

Comments
 (0)