vue3-core/packages/core/src/optional/asyncComponent.ts

112 lines
2.5 KiB
TypeScript
Raw Normal View History

2018-09-26 09:28:52 +08:00
import { ChildrenFlags } from '../flags'
import { createComponentVNode, Slots } from '../vdom'
import { Component, ComponentType, ComponentClass } from '../component'
2018-09-26 09:28:52 +08:00
export interface AsyncComponentFactory {
(): Promise<ComponentType>
resolved?: ComponentType
}
export interface AsyncComponentFullOptions {
factory: AsyncComponentFactory
loading?: ComponentType
error?: ComponentType
delay?: number
timeout?: number
}
export type AsyncComponentOptions =
| AsyncComponentFactory
| AsyncComponentFullOptions
interface AsyncContainerData {
comp: ComponentType | null
err: Error | null
delayed: boolean
2018-09-26 09:28:52 +08:00
timedOut: boolean
}
export function createAsyncComponent(
options: AsyncComponentOptions
): ComponentClass {
if (typeof options === 'function') {
options = { factory: options }
2018-09-26 09:28:52 +08:00
}
const {
factory,
timeout,
delay = 200,
loading: loadingComp,
error: errorComp
} = options
return class AsyncContainer extends Component<AsyncContainerData> {
data() {
return {
comp: null,
err: null,
delayed: false,
timedOut: false
}
2018-09-26 09:28:52 +08:00
}
// doing this in beforeMount so this is non-SSR only
beforeMount() {
if (factory.resolved) {
this.comp = factory.resolved
} else {
factory()
.then(resolved => {
this.comp = factory.resolved = resolved
})
.catch(err => {
this.err = err
})
}
if (timeout != null) {
setTimeout(() => {
this.timedOut = true
}, timeout)
}
if (delay != null) {
this.delayed = true
setTimeout(() => {
this.delayed = false
}, delay)
}
2018-09-26 09:28:52 +08:00
}
render(_: any, { props, slots }: { props: any; slots: Slots }) {
if (this.err || (this.timedOut && !this.comp)) {
const error =
this.err || new Error(`Async component timed out after ${timeout}ms.`)
return errorComp
? createComponentVNode(
errorComp,
{ error },
null,
ChildrenFlags.NO_CHILDREN
)
: null
} else if (this.comp) {
return createComponentVNode(
this.comp,
props,
slots,
ChildrenFlags.STABLE_SLOTS
)
} else {
return loadingComp && !this.delayed
? createComponentVNode(
loadingComp,
null,
null,
ChildrenFlags.NO_CHILDREN
)
: null
}
}
} as ComponentClass
2018-09-26 09:28:52 +08:00
}