Browse Source

Merge branch 'async-improvements' into dev

Evan You 9 years ago
parent
commit
e7a066fe61
2 changed files with 67 additions and 29 deletions
  1. 65 27
      src/core/vdom/helpers/resolve-async-component.js
  2. 2 2
      src/shared/util.js

+ 65 - 27
src/core/vdom/helpers/resolve-async-component.js

@@ -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

+ 2 - 2
src/shared/util.js

@@ -250,10 +250,10 @@ export function looseIndexOf (arr: Array<mixed>, val: mixed): number {
  */
 export function once (fn: Function): Function {
   let called = false
-  return () => {
+  return function () {
     if (!called) {
       called = true
-      fn()
+      fn.apply(this, arguments)
     }
   }
 }