]> git.ipfire.org Git - thirdparty/vuejs/core.git/commitdiff
fix(runtime-core): error handling for created/beforeCreate hooks
authorEvan You <yyx990803@gmail.com>
Wed, 7 Oct 2020 20:58:19 +0000 (16:58 -0400)
committerEvan You <yyx990803@gmail.com>
Wed, 7 Oct 2020 20:58:19 +0000 (16:58 -0400)
fix #2268

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

index eed679136333c7fa176a516d51134d3a80ff4f30..36e3f6fcd6c2bfae6aca211869633d914e7fe8e4 100644 (file)
@@ -181,6 +181,41 @@ describe('error handling', () => {
     expect(fn).toHaveBeenCalledWith(err, 'setup function')
   })
 
+  // unlike other lifecycle hooks, created/beforeCreate are called as part of
+  // the options API initiualization process instead of by the renderer.
+  test('in created/beforeCreate hook', () => {
+    const err = new Error('foo')
+    const fn = jest.fn()
+
+    const Comp = {
+      setup() {
+        onErrorCaptured((err, instance, info) => {
+          fn(err, info)
+          return false
+        })
+        return () => [h(Child1), h(Child2)]
+      }
+    }
+
+    const Child1 = {
+      created() {
+        throw err
+      },
+      render() {}
+    }
+
+    const Child2 = {
+      beforeCreate() {
+        throw err
+      },
+      render() {}
+    }
+
+    render(h(Comp), nodeOps.createElement('div'))
+    expect(fn).toHaveBeenCalledWith(err, 'created hook')
+    expect(fn).toHaveBeenCalledWith(err, 'beforeCreate hook')
+  })
+
   test('in render function', () => {
     const err = new Error('foo')
     const fn = jest.fn()
index 69f1f2afd1e47e849eafe9ac303094d6da066743..57bd57e3d8573b3ee3e3f1b578a7518087f254ec 100644 (file)
@@ -5,7 +5,8 @@ import {
   ComponentInternalOptions,
   Component,
   ConcreteComponent,
-  InternalRenderFunction
+  InternalRenderFunction,
+  LifecycleHooks
 } from './component'
 import {
   isFunction,
@@ -55,6 +56,7 @@ import {
 } from './componentPublicInstance'
 import { warn } from './warning'
 import { VNodeChild } from './vnode'
+import { callWithAsyncErrorHandling } from './errorHandling'
 
 /**
  * Interface for declaring custom options.
@@ -472,7 +474,13 @@ export function applyOptions(
   // applyOptions is called non-as-mixin once per instance
   if (!asMixin) {
     isInBeforeCreate = true
-    callSyncHook('beforeCreate', options, publicThis, globalMixins)
+    callSyncHook(
+      'beforeCreate',
+      LifecycleHooks.BEFORE_CREATE,
+      options,
+      instance,
+      globalMixins
+    )
     isInBeforeCreate = false
     // global mixins are applied first
     applyMixins(instance, globalMixins, deferredData, deferredWatch)
@@ -662,7 +670,13 @@ export function applyOptions(
 
   // lifecycle options
   if (!asMixin) {
-    callSyncHook('created', options, publicThis, globalMixins)
+    callSyncHook(
+      'created',
+      LifecycleHooks.CREATED,
+      options,
+      instance,
+      globalMixins
+    )
   }
   if (beforeMount) {
     onBeforeMount(beforeMount.bind(publicThis))
@@ -707,52 +721,54 @@ export function applyOptions(
 
 function callSyncHook(
   name: 'beforeCreate' | 'created',
+  type: LifecycleHooks,
   options: ComponentOptions,
-  ctx: ComponentPublicInstance,
+  instance: ComponentInternalInstance,
   globalMixins: ComponentOptions[]
 ) {
-  callHookFromMixins(name, globalMixins, ctx)
-
+  callHookFromMixins(name, type, globalMixins, instance)
   const { extends: base, mixins } = options
   if (base) {
-    callHookFromExtends(name, base, ctx)
+    callHookFromExtends(name, type, base, instance)
   }
   if (mixins) {
-    callHookFromMixins(name, mixins, ctx)
+    callHookFromMixins(name, type, mixins, instance)
   }
   const selfHook = options[name]
   if (selfHook) {
-    selfHook.call(ctx)
+    callWithAsyncErrorHandling(selfHook.bind(instance.proxy!), instance, type)
   }
 }
 
 function callHookFromExtends(
   name: 'beforeCreate' | 'created',
+  type: LifecycleHooks,
   base: ComponentOptions,
-  ctx: ComponentPublicInstance
+  instance: ComponentInternalInstance
 ) {
   if (base.extends) {
-    callHookFromExtends(name, base.extends, ctx)
+    callHookFromExtends(name, type, base.extends, instance)
   }
   const baseHook = base[name]
   if (baseHook) {
-    baseHook.call(ctx)
+    callWithAsyncErrorHandling(baseHook.bind(instance.proxy!), instance, type)
   }
 }
 
 function callHookFromMixins(
   name: 'beforeCreate' | 'created',
+  type: LifecycleHooks,
   mixins: ComponentOptions[],
-  ctx: ComponentPublicInstance
+  instance: ComponentInternalInstance
 ) {
   for (let i = 0; i < mixins.length; i++) {
     const chainedMixins = mixins[i].mixins
     if (chainedMixins) {
-      callHookFromMixins(name, chainedMixins, ctx)
+      callHookFromMixins(name, type, chainedMixins, instance)
     }
     const fn = mixins[i][name]
     if (fn) {
-      fn.call(ctx)
+      callWithAsyncErrorHandling(fn.bind(instance.proxy!), instance, type)
     }
   }
 }