|
|
@@ -1,62 +1,100 @@
|
|
|
/* @flow */
|
|
|
|
|
|
-// () => ({
|
|
|
-// component: import('./xxx.vue'),
|
|
|
-// delay: 200,
|
|
|
-// loading: LoadingComponent,
|
|
|
-// error: ErrorComponent
|
|
|
-// })
|
|
|
-
|
|
|
import {
|
|
|
warn,
|
|
|
+ once,
|
|
|
+ isDef,
|
|
|
+ isUndef,
|
|
|
+ isTrue,
|
|
|
isObject
|
|
|
} from 'core/util/index'
|
|
|
|
|
|
+function ensureCtor (comp, base) {
|
|
|
+ return isObject(comp)
|
|
|
+ ? base.extend(comp)
|
|
|
+ : comp
|
|
|
+}
|
|
|
+
|
|
|
export function resolveAsyncComponent (
|
|
|
factory: Function,
|
|
|
baseCtor: Class<Component>,
|
|
|
context: Component
|
|
|
): Class<Component> | void {
|
|
|
- if (factory.resolved) {
|
|
|
+ if (isTrue(factory.error) && isDef(factory.errorComp)) {
|
|
|
+ return factory.errorComp
|
|
|
+ }
|
|
|
+
|
|
|
+ if (isDef(factory.resolved)) {
|
|
|
return factory.resolved
|
|
|
}
|
|
|
|
|
|
- const cb = () => context.$forceUpdate()
|
|
|
- if (factory.requested) {
|
|
|
- // pool callbacks
|
|
|
- factory.pendingCallbacks.push(cb)
|
|
|
+ if (isTrue(factory.loading) && isDef(factory.loadingComp)) {
|
|
|
+ return factory.loadingComp
|
|
|
+ }
|
|
|
+
|
|
|
+ if (isDef(factory.contexts)) {
|
|
|
+ // already pending
|
|
|
+ factory.contexts.push(context)
|
|
|
} else {
|
|
|
- factory.requested = true
|
|
|
- const cbs = factory.pendingCallbacks = [cb]
|
|
|
+ const contexts = factory.contexts = [context]
|
|
|
let sync = true
|
|
|
|
|
|
- const resolve = (res: Object | Class<Component>) => {
|
|
|
- if (isObject(res)) {
|
|
|
- res = baseCtor.extend(res)
|
|
|
+ const forceRender = () => {
|
|
|
+ for (let i = 0, l = contexts.length; i < l; i++) {
|
|
|
+ contexts[i].$forceUpdate()
|
|
|
}
|
|
|
+ }
|
|
|
+
|
|
|
+ const resolve = once((res: Object | Class<Component>) => {
|
|
|
// cache resolved
|
|
|
- factory.resolved = res
|
|
|
+ factory.resolved = ensureCtor(res, baseCtor)
|
|
|
// invoke callbacks only if this is not a synchronous resolve
|
|
|
// (async resolves are shimmed as synchronous during SSR)
|
|
|
if (!sync) {
|
|
|
- for (let i = 0, l = cbs.length; i < l; i++) {
|
|
|
- cbs[i](res)
|
|
|
- }
|
|
|
+ forceRender()
|
|
|
}
|
|
|
- }
|
|
|
+ })
|
|
|
|
|
|
- const reject = reason => {
|
|
|
+ const reject = once(reason => {
|
|
|
process.env.NODE_ENV !== 'production' && warn(
|
|
|
`Failed to resolve async component: ${String(factory)}` +
|
|
|
(reason ? `\nReason: ${reason}` : '')
|
|
|
)
|
|
|
- }
|
|
|
+ if (isDef(factory.errorComp)) {
|
|
|
+ factory.error = true
|
|
|
+ forceRender()
|
|
|
+ }
|
|
|
+ })
|
|
|
|
|
|
const res = factory(resolve, reject)
|
|
|
|
|
|
- // handle promise
|
|
|
- if (res && typeof res.then === 'function' && !factory.resolved) {
|
|
|
- res.then(resolve, reject)
|
|
|
+ if (isObject(res)) {
|
|
|
+ if (typeof res.then === 'function') {
|
|
|
+ // () => Promise
|
|
|
+ if (isUndef(factory.resolved)) {
|
|
|
+ res.then(resolve, reject)
|
|
|
+ }
|
|
|
+ } else if (isDef(res.component) && typeof res.component.then === 'function') {
|
|
|
+ if (isDef(res.error)) {
|
|
|
+ factory.errorComp = ensureCtor(res.error, baseCtor)
|
|
|
+ }
|
|
|
+
|
|
|
+ if (isDef(res.loading)) {
|
|
|
+ factory.loadingComp = ensureCtor(res.loading, baseCtor)
|
|
|
+ setTimeout(() => {
|
|
|
+ if (isUndef(factory.resolved) && isUndef(factory.error)) {
|
|
|
+ factory.loading = true
|
|
|
+ forceRender()
|
|
|
+ }
|
|
|
+ }, res.delay || 200)
|
|
|
+ }
|
|
|
+
|
|
|
+ if (isDef(res.timeout)) {
|
|
|
+ setTimeout(reject, res.timeout)
|
|
|
+ }
|
|
|
+
|
|
|
+ res.component.then(resolve, reject)
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
sync = false
|