Skip to content

Commit c7b4a37

Browse files
committed
fix(runtime-core/async-component): fix error component when there are no error handlers
fix #2129
1 parent bad0ecb commit c7b4a37

File tree

3 files changed

+67
-7
lines changed

3 files changed

+67
-7
lines changed

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

+45
Original file line numberDiff line numberDiff line change
@@ -206,6 +206,51 @@ describe('api: defineAsyncComponent', () => {
206206
expect(serializeInner(root)).toBe('resolved')
207207
})
208208

209+
// #2129
210+
test('error with error component, without global handler', async () => {
211+
let resolve: (comp: Component) => void
212+
let reject: (e: Error) => void
213+
const Foo = defineAsyncComponent({
214+
loader: () =>
215+
new Promise((_resolve, _reject) => {
216+
resolve = _resolve as any
217+
reject = _reject
218+
}),
219+
errorComponent: (props: { error: Error }) => props.error.message
220+
})
221+
222+
const toggle = ref(true)
223+
const root = nodeOps.createElement('div')
224+
const app = createApp({
225+
render: () => (toggle.value ? h(Foo) : null)
226+
})
227+
228+
app.mount(root)
229+
expect(serializeInner(root)).toBe('<!---->')
230+
231+
const err = new Error('errored out')
232+
reject!(err)
233+
await timeout()
234+
expect(serializeInner(root)).toBe('errored out')
235+
expect(
236+
'Unhandled error during execution of async component loader'
237+
).toHaveBeenWarned()
238+
239+
toggle.value = false
240+
await nextTick()
241+
expect(serializeInner(root)).toBe('<!---->')
242+
243+
// errored out on previous load, toggle and mock success this time
244+
toggle.value = true
245+
await nextTick()
246+
expect(serializeInner(root)).toBe('<!---->')
247+
248+
// should render this time
249+
resolve!(() => 'resolved')
250+
await timeout()
251+
expect(serializeInner(root)).toBe('resolved')
252+
})
253+
209254
test('error with error + loading components', async () => {
210255
let resolve: (comp: Component) => void
211256
let reject: (e: Error) => void

packages/runtime-core/src/apiAsyncComponent.ts

+7-2
Original file line numberDiff line numberDiff line change
@@ -117,7 +117,12 @@ export function defineAsyncComponent<
117117

118118
const onError = (err: Error) => {
119119
pendingRequest = null
120-
handleError(err, instance, ErrorCodes.ASYNC_COMPONENT_LOADER)
120+
handleError(
121+
err,
122+
instance,
123+
ErrorCodes.ASYNC_COMPONENT_LOADER,
124+
!errorComponent /* do not throw in dev if user provided error component */
125+
)
121126
}
122127

123128
// suspense-controlled or SSR.
@@ -152,7 +157,7 @@ export function defineAsyncComponent<
152157

153158
if (timeout != null) {
154159
setTimeout(() => {
155-
if (!loaded.value) {
160+
if (!loaded.value && !error.value) {
156161
const err = new Error(
157162
`Async component timed out after ${timeout}ms.`
158163
)

packages/runtime-core/src/errorHandling.ts

+15-5
Original file line numberDiff line numberDiff line change
@@ -99,7 +99,8 @@ export function callWithAsyncErrorHandling(
9999
export function handleError(
100100
err: unknown,
101101
instance: ComponentInternalInstance | null,
102-
type: ErrorTypes
102+
type: ErrorTypes,
103+
throwInDev = true
103104
) {
104105
const contextVNode = instance ? instance.vnode : null
105106
if (instance) {
@@ -131,10 +132,15 @@ export function handleError(
131132
return
132133
}
133134
}
134-
logError(err, type, contextVNode)
135+
logError(err, type, contextVNode, throwInDev)
135136
}
136137

137-
function logError(err: unknown, type: ErrorTypes, contextVNode: VNode | null) {
138+
function logError(
139+
err: unknown,
140+
type: ErrorTypes,
141+
contextVNode: VNode | null,
142+
throwInDev = true
143+
) {
138144
if (__DEV__) {
139145
const info = ErrorTypeStrings[type]
140146
if (contextVNode) {
@@ -144,8 +150,12 @@ function logError(err: unknown, type: ErrorTypes, contextVNode: VNode | null) {
144150
if (contextVNode) {
145151
popWarningContext()
146152
}
147-
// crash in dev so it's more noticeable
148-
throw err
153+
// crash in dev by default so it's more noticeable
154+
if (throwInDev) {
155+
throw err
156+
} else {
157+
console.error(err)
158+
}
149159
} else {
150160
// recover in prod to reduce the impact on end-user
151161
console.error(err)

0 commit comments

Comments
 (0)