Skip to content

Commit b392fe4

Browse files
committed
fix(runtime-core): error handling for created/beforeCreate hooks
fix #2268
1 parent d744b8a commit b392fe4

File tree

2 files changed

+66
-15
lines changed

2 files changed

+66
-15
lines changed

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

+35
Original file line numberDiff line numberDiff line change
@@ -181,6 +181,41 @@ describe('error handling', () => {
181181
expect(fn).toHaveBeenCalledWith(err, 'setup function')
182182
})
183183

184+
// unlike other lifecycle hooks, created/beforeCreate are called as part of
185+
// the options API initiualization process instead of by the renderer.
186+
test('in created/beforeCreate hook', () => {
187+
const err = new Error('foo')
188+
const fn = jest.fn()
189+
190+
const Comp = {
191+
setup() {
192+
onErrorCaptured((err, instance, info) => {
193+
fn(err, info)
194+
return false
195+
})
196+
return () => [h(Child1), h(Child2)]
197+
}
198+
}
199+
200+
const Child1 = {
201+
created() {
202+
throw err
203+
},
204+
render() {}
205+
}
206+
207+
const Child2 = {
208+
beforeCreate() {
209+
throw err
210+
},
211+
render() {}
212+
}
213+
214+
render(h(Comp), nodeOps.createElement('div'))
215+
expect(fn).toHaveBeenCalledWith(err, 'created hook')
216+
expect(fn).toHaveBeenCalledWith(err, 'beforeCreate hook')
217+
})
218+
184219
test('in render function', () => {
185220
const err = new Error('foo')
186221
const fn = jest.fn()

packages/runtime-core/src/componentOptions.ts

+31-15
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,8 @@ import {
55
ComponentInternalOptions,
66
Component,
77
ConcreteComponent,
8-
InternalRenderFunction
8+
InternalRenderFunction,
9+
LifecycleHooks
910
} from './component'
1011
import {
1112
isFunction,
@@ -55,6 +56,7 @@ import {
5556
} from './componentPublicInstance'
5657
import { warn } from './warning'
5758
import { VNodeChild } from './vnode'
59+
import { callWithAsyncErrorHandling } from './errorHandling'
5860

5961
/**
6062
* Interface for declaring custom options.
@@ -472,7 +474,13 @@ export function applyOptions(
472474
// applyOptions is called non-as-mixin once per instance
473475
if (!asMixin) {
474476
isInBeforeCreate = true
475-
callSyncHook('beforeCreate', options, publicThis, globalMixins)
477+
callSyncHook(
478+
'beforeCreate',
479+
LifecycleHooks.BEFORE_CREATE,
480+
options,
481+
instance,
482+
globalMixins
483+
)
476484
isInBeforeCreate = false
477485
// global mixins are applied first
478486
applyMixins(instance, globalMixins, deferredData, deferredWatch)
@@ -662,7 +670,13 @@ export function applyOptions(
662670

663671
// lifecycle options
664672
if (!asMixin) {
665-
callSyncHook('created', options, publicThis, globalMixins)
673+
callSyncHook(
674+
'created',
675+
LifecycleHooks.CREATED,
676+
options,
677+
instance,
678+
globalMixins
679+
)
666680
}
667681
if (beforeMount) {
668682
onBeforeMount(beforeMount.bind(publicThis))
@@ -707,52 +721,54 @@ export function applyOptions(
707721

708722
function callSyncHook(
709723
name: 'beforeCreate' | 'created',
724+
type: LifecycleHooks,
710725
options: ComponentOptions,
711-
ctx: ComponentPublicInstance,
726+
instance: ComponentInternalInstance,
712727
globalMixins: ComponentOptions[]
713728
) {
714-
callHookFromMixins(name, globalMixins, ctx)
715-
729+
callHookFromMixins(name, type, globalMixins, instance)
716730
const { extends: base, mixins } = options
717731
if (base) {
718-
callHookFromExtends(name, base, ctx)
732+
callHookFromExtends(name, type, base, instance)
719733
}
720734
if (mixins) {
721-
callHookFromMixins(name, mixins, ctx)
735+
callHookFromMixins(name, type, mixins, instance)
722736
}
723737
const selfHook = options[name]
724738
if (selfHook) {
725-
selfHook.call(ctx)
739+
callWithAsyncErrorHandling(selfHook.bind(instance.proxy!), instance, type)
726740
}
727741
}
728742

729743
function callHookFromExtends(
730744
name: 'beforeCreate' | 'created',
745+
type: LifecycleHooks,
731746
base: ComponentOptions,
732-
ctx: ComponentPublicInstance
747+
instance: ComponentInternalInstance
733748
) {
734749
if (base.extends) {
735-
callHookFromExtends(name, base.extends, ctx)
750+
callHookFromExtends(name, type, base.extends, instance)
736751
}
737752
const baseHook = base[name]
738753
if (baseHook) {
739-
baseHook.call(ctx)
754+
callWithAsyncErrorHandling(baseHook.bind(instance.proxy!), instance, type)
740755
}
741756
}
742757

743758
function callHookFromMixins(
744759
name: 'beforeCreate' | 'created',
760+
type: LifecycleHooks,
745761
mixins: ComponentOptions[],
746-
ctx: ComponentPublicInstance
762+
instance: ComponentInternalInstance
747763
) {
748764
for (let i = 0; i < mixins.length; i++) {
749765
const chainedMixins = mixins[i].mixins
750766
if (chainedMixins) {
751-
callHookFromMixins(name, chainedMixins, ctx)
767+
callHookFromMixins(name, type, chainedMixins, instance)
752768
}
753769
const fn = mixins[i][name]
754770
if (fn) {
755-
fn.call(ctx)
771+
callWithAsyncErrorHandling(fn.bind(instance.proxy!), instance, type)
756772
}
757773
}
758774
}

0 commit comments

Comments
 (0)