@@ -15,6 +15,7 @@ import {
15
15
currentInstance ,
16
16
endMeasure ,
17
17
expose ,
18
+ getComponentName ,
18
19
nextUid ,
19
20
popWarningContext ,
20
21
pushWarningContext ,
@@ -35,7 +36,13 @@ import {
35
36
resetTracking ,
36
37
unref ,
37
38
} from '@vue/reactivity'
38
- import { EMPTY_OBJ , invokeArrayFns , isFunction , isString } from '@vue/shared'
39
+ import {
40
+ EMPTY_OBJ ,
41
+ invokeArrayFns ,
42
+ isFunction ,
43
+ isPromise ,
44
+ isString ,
45
+ } from '@vue/shared'
39
46
import {
40
47
type DynamicPropsSource ,
41
48
type RawProps ,
@@ -137,6 +144,7 @@ export function createComponent(
137
144
appContext : GenericAppContext = ( currentInstance &&
138
145
currentInstance . appContext ) ||
139
146
emptyContext ,
147
+ parentSuspense ?: SuspenseBoundary | null ,
140
148
) : VaporComponentInstance {
141
149
const _insertionParent = insertionParent
142
150
const _insertionAnchor = insertionAnchor
@@ -180,6 +188,7 @@ export function createComponent(
180
188
rawProps as RawProps ,
181
189
rawSlots as RawSlots ,
182
190
appContext ,
191
+ parentSuspense ,
183
192
)
184
193
185
194
if ( __DEV__ ) {
@@ -207,56 +216,24 @@ export function createComponent(
207
216
] ) || EMPTY_OBJ
208
217
: EMPTY_OBJ
209
218
210
- if ( __DEV__ && ! isBlock ( setupResult ) ) {
211
- if ( isFunction ( component ) ) {
212
- warn ( `Functional vapor component must return a block directly.` )
213
- instance . block = [ ]
214
- } else if ( ! component . render ) {
219
+ const isAsyncSetup = isPromise ( setupResult )
220
+ if ( __FEATURE_SUSPENSE__ && isAsyncSetup ) {
221
+ // async setup returned Promise.
222
+ // bail here and wait for re-entry.
223
+ instance . asyncDep = setupResult
224
+ if ( __DEV__ && ! instance . suspense ) {
225
+ const name = getComponentName ( component , true ) ?? 'Anonymous'
215
226
warn (
216
- `Vapor component setup() returned non-block value, and has no render function.` ,
227
+ `Component <${ name } >: setup function returned a promise, but no ` +
228
+ `<Suspense> boundary was found in the parent component tree. ` +
229
+ `A component with async setup() must be nested in a <Suspense> ` +
230
+ `in order to be rendered.` ,
217
231
)
218
- instance . block = [ ]
219
- } else {
220
- instance . devtoolsRawSetupState = setupResult
221
- // TODO make the proxy warn non-existent property access during dev
222
- instance . setupState = proxyRefs ( setupResult )
223
- devRender ( instance )
224
-
225
- // HMR
226
- if ( component . __hmrId ) {
227
- registerHMR ( instance )
228
- instance . isSingleRoot = isSingleRoot
229
- instance . hmrRerender = hmrRerender . bind ( null , instance )
230
- instance . hmrReload = hmrReload . bind ( null , instance )
231
- }
232
- }
233
- } else {
234
- // component has a render function but no setup function
235
- // (typically components with only a template and no state)
236
- if ( ! setupFn && component . render ) {
237
- instance . block = callWithErrorHandling (
238
- component . render ,
239
- instance ,
240
- ErrorCodes . RENDER_FUNCTION ,
241
- )
242
- } else {
243
- // in prod result can only be block
244
- instance . block = setupResult as Block
245
232
}
246
233
}
247
234
248
- // single root, inherit attrs
249
- if (
250
- instance . hasFallthrough &&
251
- component . inheritAttrs !== false &&
252
- instance . block instanceof Element &&
253
- Object . keys ( instance . attrs ) . length
254
- ) {
255
- renderEffect ( ( ) => {
256
- isApplyingFallthroughProps = true
257
- setDynamicProps ( instance . block as Element , [ instance . attrs ] )
258
- isApplyingFallthroughProps = false
259
- } )
235
+ if ( ! isAsyncSetup ) {
236
+ handleSetupResult ( setupResult , component , instance , isSingleRoot , setupFn )
260
237
}
261
238
262
239
resetTracking ( )
@@ -269,7 +246,7 @@ export function createComponent(
269
246
270
247
onScopeDispose ( ( ) => unmountComponent ( instance ) , true )
271
248
272
- if ( ! isHydrating && _insertionParent ) {
249
+ if ( ! isHydrating && _insertionParent && ! isAsyncSetup ) {
273
250
insert ( instance . block , _insertionParent , _insertionAnchor )
274
251
}
275
252
@@ -342,6 +319,9 @@ export class VaporComponentInstance implements GenericComponentInstance {
342
319
ids : [ string , number , number ]
343
320
// for suspense
344
321
suspense : SuspenseBoundary | null
322
+ suspenseId : number
323
+ asyncDep : Promise < any > | null
324
+ asyncResolved : boolean
345
325
346
326
hasFallthrough : boolean
347
327
@@ -380,6 +360,7 @@ export class VaporComponentInstance implements GenericComponentInstance {
380
360
rawProps ?: RawProps | null ,
381
361
rawSlots ?: RawSlots | null ,
382
362
appContext ?: GenericAppContext ,
363
+ suspense ?: SuspenseBoundary | null ,
383
364
) {
384
365
this . vapor = true
385
366
this . uid = nextUid ( )
@@ -403,12 +384,13 @@ export class VaporComponentInstance implements GenericComponentInstance {
403
384
this . emit = emit . bind ( null , this )
404
385
this . expose = expose . bind ( null , this )
405
386
this . refs = EMPTY_OBJ
406
- this . emitted =
407
- this . exposed =
408
- this . exposeProxy =
409
- this . propsDefaults =
410
- this . suspense =
411
- null
387
+ this . emitted = this . exposed = this . exposeProxy = this . propsDefaults = null
388
+
389
+ // suspense related
390
+ this . suspense = suspense !
391
+ this . suspenseId = suspense ? suspense . pendingId : 0
392
+ this . asyncDep = null
393
+ this . asyncResolved = false
412
394
413
395
this . isMounted =
414
396
this . isUnmounted =
@@ -545,3 +527,70 @@ export function getExposed(
545
527
)
546
528
}
547
529
}
530
+
531
+ export function handleSetupResult (
532
+ setupResult : any ,
533
+ component : VaporComponent ,
534
+ instance : VaporComponentInstance ,
535
+ isSingleRoot : boolean | undefined ,
536
+ setupFn : VaporSetupFn | undefined ,
537
+ ) : void {
538
+ if ( __DEV__ ) {
539
+ pushWarningContext ( instance )
540
+ }
541
+ if ( __DEV__ && ! isBlock ( setupResult ) ) {
542
+ if ( isFunction ( component ) ) {
543
+ warn ( `Functional vapor component must return a block directly.` )
544
+ instance . block = [ ]
545
+ } else if ( ! component . render ) {
546
+ warn (
547
+ `Vapor component setup() returned non-block value, and has no render function.` ,
548
+ )
549
+ instance . block = [ ]
550
+ } else {
551
+ instance . devtoolsRawSetupState = setupResult
552
+ // TODO make the proxy warn non-existent property access during dev
553
+ instance . setupState = proxyRefs ( setupResult )
554
+ devRender ( instance )
555
+
556
+ // HMR
557
+ if ( component . __hmrId ) {
558
+ registerHMR ( instance )
559
+ instance . isSingleRoot = isSingleRoot
560
+ instance . hmrRerender = hmrRerender . bind ( null , instance )
561
+ instance . hmrReload = hmrReload . bind ( null , instance )
562
+ }
563
+ }
564
+ } else {
565
+ // component has a render function but no setup function
566
+ // (typically components with only a template and no state)
567
+ if ( ! setupFn && component . render ) {
568
+ instance . block = callWithErrorHandling (
569
+ component . render ,
570
+ instance ,
571
+ ErrorCodes . RENDER_FUNCTION ,
572
+ )
573
+ } else {
574
+ // in prod result can only be block
575
+ instance . block = setupResult as Block
576
+ }
577
+ }
578
+
579
+ // single root, inherit attrs
580
+ if (
581
+ instance . hasFallthrough &&
582
+ component . inheritAttrs !== false &&
583
+ instance . block instanceof Element &&
584
+ Object . keys ( instance . attrs ) . length
585
+ ) {
586
+ renderEffect ( ( ) => {
587
+ isApplyingFallthroughProps = true
588
+ setDynamicProps ( instance . block as Element , [ instance . attrs ] )
589
+ isApplyingFallthroughProps = false
590
+ } )
591
+ }
592
+
593
+ if ( __DEV__ ) {
594
+ popWarningContext ( )
595
+ }
596
+ }
0 commit comments