fix(runtime-core): error handling for created/beforeCreate hooks
fix #2268
This commit is contained in:
		
							parent
							
								
									d744b8a2dc
								
							
						
					
					
						commit
						b392fe419c
					
				@ -181,6 +181,41 @@ describe('error handling', () => {
 | 
				
			|||||||
    expect(fn).toHaveBeenCalledWith(err, 'setup function')
 | 
					    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', () => {
 | 
					  test('in render function', () => {
 | 
				
			||||||
    const err = new Error('foo')
 | 
					    const err = new Error('foo')
 | 
				
			||||||
    const fn = jest.fn()
 | 
					    const fn = jest.fn()
 | 
				
			||||||
 | 
				
			|||||||
@ -5,7 +5,8 @@ import {
 | 
				
			|||||||
  ComponentInternalOptions,
 | 
					  ComponentInternalOptions,
 | 
				
			||||||
  Component,
 | 
					  Component,
 | 
				
			||||||
  ConcreteComponent,
 | 
					  ConcreteComponent,
 | 
				
			||||||
  InternalRenderFunction
 | 
					  InternalRenderFunction,
 | 
				
			||||||
 | 
					  LifecycleHooks
 | 
				
			||||||
} from './component'
 | 
					} from './component'
 | 
				
			||||||
import {
 | 
					import {
 | 
				
			||||||
  isFunction,
 | 
					  isFunction,
 | 
				
			||||||
@ -55,6 +56,7 @@ import {
 | 
				
			|||||||
} from './componentPublicInstance'
 | 
					} from './componentPublicInstance'
 | 
				
			||||||
import { warn } from './warning'
 | 
					import { warn } from './warning'
 | 
				
			||||||
import { VNodeChild } from './vnode'
 | 
					import { VNodeChild } from './vnode'
 | 
				
			||||||
 | 
					import { callWithAsyncErrorHandling } from './errorHandling'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/**
 | 
					/**
 | 
				
			||||||
 * Interface for declaring custom options.
 | 
					 * Interface for declaring custom options.
 | 
				
			||||||
@ -472,7 +474,13 @@ export function applyOptions(
 | 
				
			|||||||
  // applyOptions is called non-as-mixin once per instance
 | 
					  // applyOptions is called non-as-mixin once per instance
 | 
				
			||||||
  if (!asMixin) {
 | 
					  if (!asMixin) {
 | 
				
			||||||
    isInBeforeCreate = true
 | 
					    isInBeforeCreate = true
 | 
				
			||||||
    callSyncHook('beforeCreate', options, publicThis, globalMixins)
 | 
					    callSyncHook(
 | 
				
			||||||
 | 
					      'beforeCreate',
 | 
				
			||||||
 | 
					      LifecycleHooks.BEFORE_CREATE,
 | 
				
			||||||
 | 
					      options,
 | 
				
			||||||
 | 
					      instance,
 | 
				
			||||||
 | 
					      globalMixins
 | 
				
			||||||
 | 
					    )
 | 
				
			||||||
    isInBeforeCreate = false
 | 
					    isInBeforeCreate = false
 | 
				
			||||||
    // global mixins are applied first
 | 
					    // global mixins are applied first
 | 
				
			||||||
    applyMixins(instance, globalMixins, deferredData, deferredWatch)
 | 
					    applyMixins(instance, globalMixins, deferredData, deferredWatch)
 | 
				
			||||||
@ -662,7 +670,13 @@ export function applyOptions(
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
  // lifecycle options
 | 
					  // lifecycle options
 | 
				
			||||||
  if (!asMixin) {
 | 
					  if (!asMixin) {
 | 
				
			||||||
    callSyncHook('created', options, publicThis, globalMixins)
 | 
					    callSyncHook(
 | 
				
			||||||
 | 
					      'created',
 | 
				
			||||||
 | 
					      LifecycleHooks.CREATED,
 | 
				
			||||||
 | 
					      options,
 | 
				
			||||||
 | 
					      instance,
 | 
				
			||||||
 | 
					      globalMixins
 | 
				
			||||||
 | 
					    )
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
  if (beforeMount) {
 | 
					  if (beforeMount) {
 | 
				
			||||||
    onBeforeMount(beforeMount.bind(publicThis))
 | 
					    onBeforeMount(beforeMount.bind(publicThis))
 | 
				
			||||||
@ -707,52 +721,54 @@ export function applyOptions(
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
function callSyncHook(
 | 
					function callSyncHook(
 | 
				
			||||||
  name: 'beforeCreate' | 'created',
 | 
					  name: 'beforeCreate' | 'created',
 | 
				
			||||||
 | 
					  type: LifecycleHooks,
 | 
				
			||||||
  options: ComponentOptions,
 | 
					  options: ComponentOptions,
 | 
				
			||||||
  ctx: ComponentPublicInstance,
 | 
					  instance: ComponentInternalInstance,
 | 
				
			||||||
  globalMixins: ComponentOptions[]
 | 
					  globalMixins: ComponentOptions[]
 | 
				
			||||||
) {
 | 
					) {
 | 
				
			||||||
  callHookFromMixins(name, globalMixins, ctx)
 | 
					  callHookFromMixins(name, type, globalMixins, instance)
 | 
				
			||||||
 | 
					 | 
				
			||||||
  const { extends: base, mixins } = options
 | 
					  const { extends: base, mixins } = options
 | 
				
			||||||
  if (base) {
 | 
					  if (base) {
 | 
				
			||||||
    callHookFromExtends(name, base, ctx)
 | 
					    callHookFromExtends(name, type, base, instance)
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
  if (mixins) {
 | 
					  if (mixins) {
 | 
				
			||||||
    callHookFromMixins(name, mixins, ctx)
 | 
					    callHookFromMixins(name, type, mixins, instance)
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
  const selfHook = options[name]
 | 
					  const selfHook = options[name]
 | 
				
			||||||
  if (selfHook) {
 | 
					  if (selfHook) {
 | 
				
			||||||
    selfHook.call(ctx)
 | 
					    callWithAsyncErrorHandling(selfHook.bind(instance.proxy!), instance, type)
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
function callHookFromExtends(
 | 
					function callHookFromExtends(
 | 
				
			||||||
  name: 'beforeCreate' | 'created',
 | 
					  name: 'beforeCreate' | 'created',
 | 
				
			||||||
 | 
					  type: LifecycleHooks,
 | 
				
			||||||
  base: ComponentOptions,
 | 
					  base: ComponentOptions,
 | 
				
			||||||
  ctx: ComponentPublicInstance
 | 
					  instance: ComponentInternalInstance
 | 
				
			||||||
) {
 | 
					) {
 | 
				
			||||||
  if (base.extends) {
 | 
					  if (base.extends) {
 | 
				
			||||||
    callHookFromExtends(name, base.extends, ctx)
 | 
					    callHookFromExtends(name, type, base.extends, instance)
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
  const baseHook = base[name]
 | 
					  const baseHook = base[name]
 | 
				
			||||||
  if (baseHook) {
 | 
					  if (baseHook) {
 | 
				
			||||||
    baseHook.call(ctx)
 | 
					    callWithAsyncErrorHandling(baseHook.bind(instance.proxy!), instance, type)
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
function callHookFromMixins(
 | 
					function callHookFromMixins(
 | 
				
			||||||
  name: 'beforeCreate' | 'created',
 | 
					  name: 'beforeCreate' | 'created',
 | 
				
			||||||
 | 
					  type: LifecycleHooks,
 | 
				
			||||||
  mixins: ComponentOptions[],
 | 
					  mixins: ComponentOptions[],
 | 
				
			||||||
  ctx: ComponentPublicInstance
 | 
					  instance: ComponentInternalInstance
 | 
				
			||||||
) {
 | 
					) {
 | 
				
			||||||
  for (let i = 0; i < mixins.length; i++) {
 | 
					  for (let i = 0; i < mixins.length; i++) {
 | 
				
			||||||
    const chainedMixins = mixins[i].mixins
 | 
					    const chainedMixins = mixins[i].mixins
 | 
				
			||||||
    if (chainedMixins) {
 | 
					    if (chainedMixins) {
 | 
				
			||||||
      callHookFromMixins(name, chainedMixins, ctx)
 | 
					      callHookFromMixins(name, type, chainedMixins, instance)
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    const fn = mixins[i][name]
 | 
					    const fn = mixins[i][name]
 | 
				
			||||||
    if (fn) {
 | 
					    if (fn) {
 | 
				
			||||||
      fn.call(ctx)
 | 
					      callWithAsyncErrorHandling(fn.bind(instance.proxy!), instance, type)
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user