]> git.ipfire.org Git - thirdparty/vuejs/core.git/commitdiff
fix(runtime-core/async-component): fix error component when there are no error handlers
authorEvan You <yyx990803@gmail.com>
Wed, 16 Sep 2020 15:10:16 +0000 (11:10 -0400)
committerEvan You <yyx990803@gmail.com>
Wed, 16 Sep 2020 15:10:16 +0000 (11:10 -0400)
fix #2129

packages/runtime-core/__tests__/apiAsyncComponent.spec.ts
packages/runtime-core/src/apiAsyncComponent.ts
packages/runtime-core/src/errorHandling.ts

index 0ba6079ab3da13b630cd10723c97df87e5f260ad..af78ee7e0c97b27194abe1f470fc001056afb147 100644 (file)
@@ -206,6 +206,51 @@ describe('api: defineAsyncComponent', () => {
     expect(serializeInner(root)).toBe('resolved')
   })
 
+  // #2129
+  test('error with error component, without global handler', async () => {
+    let resolve: (comp: Component) => void
+    let reject: (e: Error) => void
+    const Foo = defineAsyncComponent({
+      loader: () =>
+        new Promise((_resolve, _reject) => {
+          resolve = _resolve as any
+          reject = _reject
+        }),
+      errorComponent: (props: { error: Error }) => props.error.message
+    })
+
+    const toggle = ref(true)
+    const root = nodeOps.createElement('div')
+    const app = createApp({
+      render: () => (toggle.value ? h(Foo) : null)
+    })
+
+    app.mount(root)
+    expect(serializeInner(root)).toBe('<!---->')
+
+    const err = new Error('errored out')
+    reject!(err)
+    await timeout()
+    expect(serializeInner(root)).toBe('errored out')
+    expect(
+      'Unhandled error during execution of async component loader'
+    ).toHaveBeenWarned()
+
+    toggle.value = false
+    await nextTick()
+    expect(serializeInner(root)).toBe('<!---->')
+
+    // errored out on previous load, toggle and mock success this time
+    toggle.value = true
+    await nextTick()
+    expect(serializeInner(root)).toBe('<!---->')
+
+    // should render this time
+    resolve!(() => 'resolved')
+    await timeout()
+    expect(serializeInner(root)).toBe('resolved')
+  })
+
   test('error with error + loading components', async () => {
     let resolve: (comp: Component) => void
     let reject: (e: Error) => void
index 3f668a4624287a8b22a7798357b6ba93784a258b..a4c468674421edc4d44dcfd9717a4e5ba0a8d909 100644 (file)
@@ -117,7 +117,12 @@ export function defineAsyncComponent<
 
       const onError = (err: Error) => {
         pendingRequest = null
-        handleError(err, instance, ErrorCodes.ASYNC_COMPONENT_LOADER)
+        handleError(
+          err,
+          instance,
+          ErrorCodes.ASYNC_COMPONENT_LOADER,
+          !errorComponent /* do not throw in dev if user provided error component */
+        )
       }
 
       // suspense-controlled or SSR.
@@ -152,7 +157,7 @@ export function defineAsyncComponent<
 
       if (timeout != null) {
         setTimeout(() => {
-          if (!loaded.value) {
+          if (!loaded.value && !error.value) {
             const err = new Error(
               `Async component timed out after ${timeout}ms.`
             )
index a922e964e5d50e4a022d8b06485afa7d229fa884..b9896c58e190096b164f8ff0c5f779e5e72972be 100644 (file)
@@ -99,7 +99,8 @@ export function callWithAsyncErrorHandling(
 export function handleError(
   err: unknown,
   instance: ComponentInternalInstance | null,
-  type: ErrorTypes
+  type: ErrorTypes,
+  throwInDev = true
 ) {
   const contextVNode = instance ? instance.vnode : null
   if (instance) {
@@ -131,10 +132,15 @@ export function handleError(
       return
     }
   }
-  logError(err, type, contextVNode)
+  logError(err, type, contextVNode, throwInDev)
 }
 
-function logError(err: unknown, type: ErrorTypes, contextVNode: VNode | null) {
+function logError(
+  err: unknown,
+  type: ErrorTypes,
+  contextVNode: VNode | null,
+  throwInDev = true
+) {
   if (__DEV__) {
     const info = ErrorTypeStrings[type]
     if (contextVNode) {
@@ -144,8 +150,12 @@ function logError(err: unknown, type: ErrorTypes, contextVNode: VNode | null) {
     if (contextVNode) {
       popWarningContext()
     }
-    // crash in dev so it's more noticeable
-    throw err
+    // crash in dev by default so it's more noticeable
+    if (throwInDev) {
+      throw err
+    } else {
+      console.error(err)
+    }
   } else {
     // recover in prod to reduce the impact on end-user
     console.error(err)