Просмотр исходного кода

feat(asyncComponent): add `onError` option for defineAsyncComponent

BREAKING CHANGE: `retryWhen` and `maxRetries` options for
`defineAsyncComponent` has been replaced by the more flexible `onError`
option, per https://github.com/vuejs/rfcs/pull/148
Evan You 6 лет назад
Родитель
Сommit
e804463492

+ 22 - 5
packages/runtime-core/__tests__/apiAsyncComponent.spec.ts

@@ -488,7 +488,13 @@ describe('api: defineAsyncComponent', () => {
           reject = _reject
         })
       },
-      retryWhen: error => error.message.match(/foo/)
+      onError(error, retry, fail) {
+        if (error.message.match(/foo/)) {
+          retry()
+        } else {
+          fail()
+        }
+      }
     })
 
     const root = nodeOps.createElement('div')
@@ -526,7 +532,13 @@ describe('api: defineAsyncComponent', () => {
           reject = _reject
         })
       },
-      retryWhen: error => error.message.match(/bar/)
+      onError(error, retry, fail) {
+        if (error.message.match(/bar/)) {
+          retry()
+        } else {
+          fail()
+        }
+      }
     })
 
     const root = nodeOps.createElement('div')
@@ -549,7 +561,7 @@ describe('api: defineAsyncComponent', () => {
     expect(serializeInner(root)).toBe('<!---->')
   })
 
-  test('retry (fail w/ maxRetries)', async () => {
+  test('retry (fail w/ max retry attempts)', async () => {
     let loaderCallCount = 0
     let reject: (e: Error) => void
 
@@ -560,8 +572,13 @@ describe('api: defineAsyncComponent', () => {
           reject = _reject
         })
       },
-      retryWhen: error => error.message.match(/foo/),
-      maxRetries: 1
+      onError(error, retry, fail, attempts) {
+        if (error.message.match(/foo/) && attempts <= 1) {
+          retry()
+        } else {
+          fail()
+        }
+      }
     })
 
     const root = nodeOps.createElement('div')

+ 16 - 9
packages/runtime-core/src/apiAsyncComponent.ts

@@ -5,7 +5,7 @@ import {
   ComponentInternalInstance,
   isInSSRComponentSetup
 } from './component'
-import { isFunction, isObject, NO } from '@vue/shared'
+import { isFunction, isObject } from '@vue/shared'
 import { ComponentPublicInstance } from './componentProxy'
 import { createVNode } from './vnode'
 import { defineComponent } from './apiDefineComponent'
@@ -27,9 +27,13 @@ export interface AsyncComponentOptions<T = any> {
   errorComponent?: PublicAPIComponent
   delay?: number
   timeout?: number
-  retryWhen?: (error: Error) => any
-  maxRetries?: number
   suspensible?: boolean
+  onError?: (
+    error: Error,
+    retry: () => void,
+    fail: () => void,
+    attempts: number
+  ) => any
 }
 
 export function defineAsyncComponent<
@@ -45,16 +49,15 @@ export function defineAsyncComponent<
     errorComponent: errorComponent,
     delay = 200,
     timeout, // undefined = never times out
-    retryWhen = NO,
-    maxRetries = 3,
-    suspensible = true
+    suspensible = true,
+    onError: userOnError
   } = source
 
   let pendingRequest: Promise<Component> | null = null
   let resolvedComp: Component | undefined
 
   let retries = 0
-  const retry = (error?: unknown) => {
+  const retry = () => {
     retries++
     pendingRequest = null
     return load()
@@ -67,8 +70,12 @@ export function defineAsyncComponent<
       (thisRequest = pendingRequest = loader()
         .catch(err => {
           err = err instanceof Error ? err : new Error(String(err))
-          if (retryWhen(err) && retries < maxRetries) {
-            return retry(err)
+          if (userOnError) {
+            return new Promise((resolve, reject) => {
+              const userRetry = () => resolve(retry())
+              const userFail = () => reject(err)
+              userOnError(err, userRetry, userFail, retries + 1)
+            })
           } else {
             throw err
           }