错误边界
<NuxtErrorBoundary> 用于捕获子组件中的运行时错误,防止错误向上冒泡导致整个页面崩溃。它让页面可以"局部降级"——某个组件出错了,其他部分仍然正常工作。
为什么需要错误边界?
| 没有 NuxtErrorBoundary | 有 NuxtErrorBoundary |
|---|---|
| 一个组件崩溃 → 整个页面白屏 | 一个组件崩溃 → 显示 fallback,其他正常 |
| 用户必须刷新页面 | 用户可以重试或继续使用 |
| 错误影响所有功能 | 错误被隔离在局部 |
错误边界的核心思想
隔离错误——把错误限制在最小范围内,不影响页面的其他部分。
类比
飞机的隔舱设计——一个舱室失压不会影响整个飞机。NuxtErrorBoundary 就是组件的隔舱。
基本用法
vue
<template>
<NuxtErrorBoundary>
<!-- 可能出错的组件 -->
<UnstableComponent />
<!-- 错误时的 fallback -->
<template #error="{ error, clearError }">
<div class="error-fallback">
<p>组件加载失败:{{ error?.message }}</p>
<button @click="clearError">重试</button>
</div>
</template>
</NuxtErrorBoundary>
</template>NuxtErrorBoundary 的工作原理
- 正常情况:渲染子组件
- 子组件抛出运行时错误:捕获错误,渲染
#error插槽 - 调用
clearError():重新渲染子组件(重试)
#error 插槽
#error 插槽提供两个参数:
| 参数 | 类型 | 说明 |
|---|---|---|
error | Error | undefined | 捕获的错误对象 |
clearError | () => void | 清除错误,重新渲染子组件 |
自定义 fallback 样式
vue
<template>
<NuxtErrorBoundary>
<ChartComponent :data="chartData" />
<template #error="{ error, clearError }">
<div class="chart-error">
<div class="error-icon">📊</div>
<p>图表加载失败</p>
<p class="error-detail">{{ error?.message }}</p>
<button @click="clearError" class="retry-btn">重新加载</button>
</div>
</template>
</NuxtErrorBoundary>
</template>事件处理
通过 @error 事件监听错误,用于上报到监控系统:
vue
<template>
<NuxtErrorBoundary @error="onError">
<MapComponent />
</NuxtErrorBoundary>
</template>
<script setup>
function onError(error) {
console.error('组件错误:', error)
// 上报到 Sentry / 其他监控
reportToSentry(error)
}
</script>@error 事件的用途
- 记录错误日志
- 上报到错误监控(Sentry、Bugsnag 等)
- 发送告警通知
@error 和 #error 插槽的区别
@error:事件回调,用于处理错误(日志、上报)#error插槽:UI 模板,用于显示 fallback
完整示例——仪表盘页面
vue
<template>
<div class="dashboard">
<h1>仪表盘</h1>
<!-- 统计卡片:独立错误边界(失败不影响其他部分) -->
<NuxtErrorBoundary @error="handleStatsError">
<StatsCards />
<template #error>
<div class="card-error">统计数据加载失败</div>
</template>
</NuxtErrorBoundary>
<!-- 图表:独立错误边界 -->
<NuxtErrorBoundary @error="handleChartError">
<LazyChartComponent />
<template #error="{ clearError }">
<div class="chart-error">
<p>图表加载失败</p>
<button @click="clearError">重试</button>
</div>
</template>
</NuxtErrorBoundary>
<!-- 数据表格:独立错误边界 -->
<NuxtErrorBoundary @error="handleTableError">
<DataTable :data="tableData" />
<template #error>
<div class="table-error">数据表格加载失败</div>
</template>
</NuxtErrorBoundary>
</div>
</template>
<script setup>
function handleStatsError(error) {
console.error('Stats error:', error)
reportToSentry(error)
}
function handleChartError(error) {
console.error('Chart error:', error)
reportToSentry(error)
}
function handleTableError(error) {
console.error('Table error:', error)
reportToSentry(error)
}
</script>这个示例展示了错误边界的核心价值
- 图表崩溃 → 统计卡片和表格仍然正常
- 表格崩溃 → 图表和统计卡片仍然正常
- 每个区域独立容错,互不影响
推荐模式
仪表盘、后台管理等包含多个独立模块的页面,每个模块都用 NuxtErrorBoundary 包裹。
NuxtErrorBoundary vs error.vue
| 特性 | NuxtErrorBoundary | error.vue |
|---|---|---|
| 作用范围 | 子组件 | 全局 |
| 错误类型 | 运行时错误 | 所有错误 |
| 显示方式 | 局部 fallback | 全屏错误页面 |
| 恢复方式 | clearError() | clearError({ redirect }) |
| 可定制性 | 高(每个边界独立) | 低(统一页面) |
| 对用户体验的影响 | 小(局部降级) | 大(整个页面替换) |
| 适用场景 | 组件级容错 | 全局兜底 |
两者配合使用
NuxtErrorBoundary:第一道防线,捕获组件级错误,局部降级error.vue:最后防线,捕获所有未处理的错误,全屏展示
防御策略
- 优先用
try/catch处理已知可能的错误(如 API 调用) - 用
NuxtErrorBoundary包裹可能不稳定的组件 error.vue作为最后的兜底
使用场景
| 场景 | 原因 | 示例 |
|---|---|---|
| 第三方组件 | 可能不稳定 | 地图、图表、富文本编辑器 |
| 浏览器 API 依赖 | SSR 时可能报错 | window、document、canvas |
| 重交互组件 | 复杂交互容易出错 | 拖拽、画布、3D 渲染 |
| 模块化容错 | 各区域独立 | 仪表盘、后台管理 |
| 懒加载组件 | 加载可能失败 | LazyXxx 组件 |
注意事项
- 只能捕获子组件错误:不能捕获自身或父组件的错误
- 异步错误不捕获:
setTimeout、Promise中的错误不会触发(需用try/catch) - Hydration 错误:部分 Hydration 不匹配错误可能不触发
clearError()会重新渲染组件:如果组件的 bug 没有修复,可能再次触发错误
知识脉络
text
错误页面 → 错误创建与抛出 → 错误清除 → 你在这里:错误边界
│
├─→ 相关:内置组件速查 → NuxtErrorBoundary
│
└─→ 相关:插件与中间件(全局错误处理)