Skip to content

Commit

Permalink
fix(runtime-core): errors during component patch should be caught by …
Browse files Browse the repository at this point in the history
…error handlers
  • Loading branch information
yyx990803 committed Jul 11, 2024
1 parent 3d34f40 commit ee0248a
Show file tree
Hide file tree
Showing 4 changed files with 43 additions and 10 deletions.
30 changes: 30 additions & 0 deletions packages/runtime-core/__tests__/errorHandling.spec.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import {
type VNode,
createApp,
defineComponent,
h,
Expand All @@ -11,6 +12,7 @@ import {
watch,
watchEffect,
} from '@vue/runtime-test'
import { ErrorCodes, ErrorTypeStrings } from '../src/errorHandling'

describe('error handling', () => {
test('propagation', () => {
Expand Down Expand Up @@ -609,5 +611,33 @@ describe('error handling', () => {
expect(handler).toHaveBeenCalledTimes(1)
})

test('errors in scheduler job with owner instance should be caught', async () => {
let vnode: VNode
const x = ref(0)
const app = createApp({
render() {
return (vnode = vnode || h('div', x.value))
},
})

app.config.errorHandler = vi.fn()
app.mount(nodeOps.createElement('div'))

const error = new Error('error')
Object.defineProperty(vnode!, 'el', {
get() {
throw error
},
})

x.value++
await nextTick()
expect(app.config.errorHandler).toHaveBeenCalledWith(
error,
{},
ErrorTypeStrings[ErrorCodes.COMPONENT_UPDATE],
)
})

// native event handler handling should be tested in respective renderers
})
10 changes: 5 additions & 5 deletions packages/runtime-core/src/errorHandling.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ export enum ErrorCodes {
FUNCTION_REF,
ASYNC_COMPONENT_LOADER,
SCHEDULER,
COMPONENT_UPDATE,
}

export const ErrorTypeStrings: Record<LifecycleHooks | ErrorCodes, string> = {
Expand Down Expand Up @@ -54,16 +55,15 @@ export const ErrorTypeStrings: Record<LifecycleHooks | ErrorCodes, string> = {
[ErrorCodes.APP_WARN_HANDLER]: 'app warnHandler',
[ErrorCodes.FUNCTION_REF]: 'ref function',
[ErrorCodes.ASYNC_COMPONENT_LOADER]: 'async component loader',
[ErrorCodes.SCHEDULER]:
'scheduler flush. This is likely a Vue internals bug. ' +
'Please open an issue at https://github.com/vuejs/core .',
[ErrorCodes.SCHEDULER]: 'scheduler flush',
[ErrorCodes.COMPONENT_UPDATE]: 'component update',
}

export type ErrorTypes = LifecycleHooks | ErrorCodes

export function callWithErrorHandling(
fn: Function,
instance: ComponentInternalInstance | null,
instance: ComponentInternalInstance | null | undefined,
type: ErrorTypes,
args?: unknown[],
) {
Expand Down Expand Up @@ -105,7 +105,7 @@ export function callWithAsyncErrorHandling(

export function handleError(
err: unknown,
instance: ComponentInternalInstance | null,
instance: ComponentInternalInstance | null | undefined,
type: ErrorTypes,
throwInDev = true,
) {
Expand Down
2 changes: 1 addition & 1 deletion packages/runtime-core/src/renderer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1587,6 +1587,7 @@ function baseCreateRenderer(
effect.run()
}
})
update.i = instance
update.id = instance.uid
// allowRecurse
// #1801, #2043 component render effects should allow recursive updates
Expand All @@ -1599,7 +1600,6 @@ function baseCreateRenderer(
effect.onTrigger = instance.rtg
? e => invokeArrayFns(instance.rtg!, e)
: void 0
update.ownerInstance = instance
}

update()
Expand Down
11 changes: 7 additions & 4 deletions packages/runtime-core/src/scheduler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,9 +26,8 @@ export interface SchedulerJob extends Function {
/**
* Attached by renderer.ts when setting up a component's render effect
* Used to obtain component information when reporting max recursive updates.
* dev only.
*/
ownerInstance?: ComponentInternalInstance
i?: ComponentInternalInstance
}

export type SchedulerJobs = SchedulerJob | SchedulerJob[]
Expand Down Expand Up @@ -240,7 +239,11 @@ function flushJobs(seen?: CountMap) {
if (__DEV__ && check(job)) {
continue
}
callWithErrorHandling(job, null, ErrorCodes.SCHEDULER)
callWithErrorHandling(
job,
job.i,
job.i ? ErrorCodes.COMPONENT_UPDATE : ErrorCodes.SCHEDULER,
)
}
}
} finally {
Expand All @@ -265,7 +268,7 @@ function checkRecursiveUpdates(seen: CountMap, fn: SchedulerJob) {
} else {
const count = seen.get(fn)!
if (count > RECURSION_LIMIT) {
const instance = fn.ownerInstance
const instance = fn.i
const componentName = instance && getComponentName(instance.type)
handleError(
`Maximum recursive updates exceeded${
Expand Down

0 comments on commit ee0248a

Please sign in to comment.