Vue 实例有一个 onErrorCaptured
钩子,每当事件处理程序或生命周期钩子抛出错误时,Vue 会调用该钩子。
例如,下面的代码将增加一个计数器,每次单击按钮时,组件 test
都会抛出一个错误。
<template>
<span id="count">{{ count }}</span>
<test></test>
</template>
<script setup>
import { onErrorCaptured, ref } from 'vue'
import test from './test.vue'
const count = ref(0)
onErrorCaptured((err) => {
console.log('Caught error', err.message)
++count.value
return false
})
</script>
test.vue
组件内容如下:
<!-- 这里我们没有提供 notAMethod 方法供按钮调用 -->
<template>
<button @click="notAMethod()">Throw</button>
</template>
按照上面的步骤走,您将在控制台看到 onErrorCaptured
捕获的错误:
Caught error _ctx.notAMethod is not a function
一个常见的问题是,当错误发生在注册 onErrorCaptured
钩子的同一组件中时,Vue 不会调用 onErrorCaptured
。
例如,如果从上述示例中删除 test
组件,并将按钮内联到顶级 Vue 实例中,Vue 将不会调用 onErrorCaptured
。
<template>
<span id="count">{{ count }}</span>
<button @click="notAMethod">Throw</button>
</template>
<script setup>
import { onErrorCaptured, ref } from 'vue'
import test from './test.vue'
const count = ref(0)
// Vue 不会调这个钩子,因为错误发生在这个 Vue 实例中,而不是子组件。
onErrorCaptured((err) => {
console.log('Caught error', err.message)
++count.value
return false
})
</script>
好的一面是,当异步函数抛出错误时,Vue 会调用 onErrorCapture
。
例如,如果子组件异步抛出错误,Vue 仍然会将错误冒泡给父组件。
<template>
<span id="count">{{ count }}</span>
<test />
</template>
<script setup>
import { onErrorCaptured, ref } from 'vue'
import test from './test.vue'
const count = ref(0)
onErrorCaptured((err) => {
console.log('Caught error', err.message)
++count.value
return false
})
</script>
test.vue
:
<template>
<button @click="test">Throw</button>
</template>
<script setup>
// Vue 会将异步错误冒泡到父级的 onErrorCaptured(),因此每次单击该按钮时,
// Vue 都会调用带有 err 的 onErrorCaptured() 钩子。
// err.message = 'Oops'
const test = async () => {
await new Promise((resolve) => setTimeout(resolve, 50))
throw new Error('Oops!')
}
</script>
控制台将打印:
Caught error Oops!
在前面的示例中,您可能已经注意到 return false
。如果 onErrorCaptured()
函数没有 return false
,则 Vue 会将错误冒泡到父组件的 onErrorCaptured()
:
<template>
<span id="count">{{ count }}</span>
<test1 />
</template>
<script setup>
import { onErrorCaptured, ref } from 'vue'
import test1 from './test1.vue'
// 由于 test1 组件的 onErrorCaptured() 没有 return false,Vue 将冒泡显示错误。
const count = ref(0)
onErrorCaptured((err) => {
console.log('Caught top-test error', err.message)
++count.value
return false
})
</script>
test1.vue
:
<template>
<test2 />
</template>
<script setup>
import { onErrorCaptured, ref } from 'vue'
import test2 from './test2.vue'
onErrorCaptured((err) => {
console.log('test 1 error', err.message)
})
</script>
test2.vue
:
<template>
<button @click="notAMethod()">Throw</button>
</template>
另一方面,如果 onErrorCaptured()
方法使用 return false
,Vue 将停止该错误的传播:
// test2.vue
onErrorCaptured((err) => {
console.log('test 1 error', err.message)
return false
})
您可以继续阅读 Handling Errors in Vue with Error Boundaries 这篇文章,它利用 onErrorCaptured
钩子封装了一个类似 React ErrorBoundary
的组件。组件地址。