Opt-out of suspense after initial loading
What problem does this feature solve?
In my scenario, several dynamic async components can be added by the user via drag'n'drop. I have a <suspense>
at the top of my component tree, showing a loading spinner while my async components are initially loaded.
Now, the user might add another dynamic async component to the tree, and while this new component is loading, I also want to render a loading spinner, not at the top level but just instead of the newly added component.
This is not an uncommon use case, I think. Needing a suspense when loading an initial state and then needing to get rid of it for additionally loaded components.
Now while the suspense feature itself is really cool and simple, the described behavior is currently only possible via ugly workarounds (<suspense v-if="...">
) that lead to much code duplication.
I can either have suspensible: true
and use the suspense, once and for all, or I can have suspensible: false
and use the loadingComponent
option to defineAsyncComponent()
- in this case I can't benefit of the suspense and end up with multiple spinners again, during the initial loading.
Two possible solutions come to my mind:
A
once
prop on the<suspense>
, telling the suspense component to give loading control back to the child components once it has resolved.Opt-out of suspense control dynamically. This seems to me as the easiest way as it would only be necessary to allow for a
ref
being passed as thesuspensible
option. This way I could provide a dynamic value todefineComponent
'ssetup
method, being evaluated only whensetup
runs and not on app init, when all the calls todefineAsyncComponent
happen.
What does the proposed API look like?
Variant 1)
<suspense once>
<!-- ... dynamic async child components, once the suspense is resolved, the get control over their loading behavior -->
</suspense>
Variant 2)
const suspensible = ref(true) // set to false after suspense is resolved
myComponent: defineAsyncComponent({
loader: () => import('@/path/to/MyComponent.vue'),
loadingComponent: MySpinner,
suspensible: suspensible
})
If you happen to appreciate this feature, I might be able to propose a PR, at least for the second variant :smile:
Found a workaround by using Suspense
with component :is
, replacing the suspense with a dummy wrapper once it has resolved.
<component
:is="suspenseResolved ? 'SimpleWrapper' : 'suspense'"
@resolve="suspenseResolved = true"
>
<my-async-tree-root-node />
<template #fallback>
<loading-component />
</template>
</component>
import { Suspense } from 'vue'
export default {
components: {
...,
Suspense,
SimpleWrapper: { template: '<div><slot></slot></div>' }
},
data() {
return {
suspenseResolved: false
}
}
}
Would appreciate a cleaner approach, though.