先记住这个答案
onErrorCaptured 可以接收后代组件渲染、setup、监听和生命周期等来源传播来的错误,并得到 error、出错实例与来源信息。默认错误会继续沿父链传播,也会交给 app.config.errorHandler;某个捕获钩子返回 false,则表示该错误已处理,阻止后续父级捕获钩子和全局 handler 接收它。边界若切换降级状态,必须保证不再渲染原来会抛错的内容,否则会形成新的渲染错误循环。生产环境 info 可能是缩写码。
- 只捕获后代错误,不负责自身同钩子错误
- 返回 false 会停止该错误继续传播
- 降级界面必须移除原出错渲染路径
返回值决定同一错误是否继续冒泡
若钩子不返回 false,父链上的多个 errorCaptured 会从近到远依次得到错误,应用全局 handler 也能统一上报。这样局部边界可以展示降级,同时中央监控仍保留记录。
返回 false 适合已经完整处理且不希望重复报告的错误,但也可能让全局监控失去信息。项目应明确哪些边界负责上报,避免局部记录失败后又阻断唯一的中央通道。
用两层边界验证传播停止
下例 FailingChild 在渲染时抛错,Boundary 捕获后写入记录并返回 false;Outer 也注册捕获,但不应收到同一个错误。真实边界还需要渲染安全后备内容。
捕获回调里若设置 failed,应让下一次渲染选择不会再访问 FailingChild 的分支。若后备组件也抛错,该新错误会按它自己的组件关系继续处理,不能认为 false 永久关闭所有错误。
import { h, onErrorCaptured, ref } from 'vue'
const errorEvents: string[] = []
const FailingChild = { setup: () => () => { throw new Error('broken child') } }
const Boundary = {
setup() {
const failed = ref(false)
onErrorCaptured(error => {
errorEvents.push('inner:' + String(error))
failed.value = true
return false
})
return () => failed.value ? h('p', 'fallback') : h(FailingChild)
}
}
const Outer = {
setup() {
onErrorCaptured(() => { errorEvents.push('outer') })
return () => h(Boundary)
}
}挂载 Outer 后,记录只包含 inner 的错误信息,最终节点显示 fallback。返回 false 阻止 outer 收到该错误;示例不配置全局 handler,生产策略仍需决定在哪里记录堆栈。
错误边界需要避免二次失败
捕获函数中的日志客户端、序列化或状态写入本身也可能抛错。Vue 会把捕获钩子的新错误与原错误交给应用级 handler,因此上报代码应尽量小、容错并避免读取不安全对象。
不要把整个应用包在一个只显示空白的边界里。按功能块提供可操作的重试或返回入口,同时保留请求编号、路由和版本等诊断上下文;敏感数据在进入日志前要过滤。
容易答错的地方
- 认为返回 true 才表示已处理
- 这个 API 的特殊控制值是 false;返回 undefined 会继续传播。团队封装边界时应显式写出决策,避免维护者根据其他事件 API 的惯例猜测。
- 捕获后仍渲染同一个失败子树
- 设置错误状态却没有让渲染分支避开原内容,会在更新时再次抛错。后备路径必须独立、安全,并限制自动重试次数以免形成错误风暴。
面试官还会怎么问?
onErrorCaptured 能捕获自己组件 render 的错误吗?
它面向从后代传播的错误,自身错误应由更高层边界或应用级 handler 处理。边界组件本身必须保持简单,否则最需要降级时可能没有可用接收者。
返回 false 后全局 errorHandler 还会收到吗?
不会收到这个被明确阻止继续传播的错误。若需要中央上报,可以在局部边界先调用可靠的记录入口再返回 false,或允许错误继续到全局统一处理。
异步请求 reject 会自动被它捕获吗?
只有进入 Vue 管理调用链的错误来源才按相应机制处理。自行启动且无人 await 或 catch 的 Promise 不能一概依赖组件边界,应在请求流程显式处理失败与取消。
参考资料
示例用于理解所注明的运行环境与边界;延伸学习可结合原文中的更多案例。