错误边界
加错误边界和全局错误处理,崩溃显示恢复路径非白屏。
为什么
错误白屏是最差 UX——用户不知发生什么、无法恢复、不信任应用安全。错误边界捕获崩溃、显示友好消息带'重试'按钮、记带足够上下文调试的错误。无边界,单组件 bug 杀整页;有边界,仅坏组件显错,页其余工作。全局 handler 捕未处理 promise 拒绝并在变静默失败前报告。这是'应用崩了我丢工作'和'一卡加载失败,点重试'的区别。
何时使用
第一个用户路由之前。约 1-2 小时。永不跳过——每应用需要。
如何做
1. 建可复用 ErrorBoundary 组件(类组件——hook 不能捕错误)
- getDerivedStateFromError 设 hasError=true;componentDidCatch 记 Sentry
- 渲染带错误消息 + 重置状态'重试'按钮的 fallback UI
2. 每路由段用错误边界包(Next.js error.tsx)
- app/dashboard/error.tsx 捕 dashboard 路由错误
- 边界近数据源,仅坏段失败
3. 加全局 handler 捕未处理 promise 拒绝
- window.addEventListener("unhandledrejection", (e) => reportError(e.reason))
- 捕无 try/catch 或 .catch() 处理的异步错误
4. 加全局错误页(Next.js global-error.tsx)供根级错误
- 替整个布局,须含 <html> 和 <body>
- 保持最小:'出错了' + 重载按钮 + 错误 ID
5. 带上下文记错误:路由、用户、动作、栈追踪
- 发 Sentry/PostHog 带唯一错误 ID 供支持引用示例
"use client";
import { Component, type ReactNode } from "react";
export class ErrorBoundary extends Component<{ children: ReactNode; fallback?: ReactNode }, { hasError: boolean }> {
state = { hasError: false };
static getDerivedStateFromError() { return { hasError: true }; }
componentDidCatch(error: Error, info: unknown) { Sentry.captureException(error, { extra: info }); }
render() {
if (this.state.hasError) return this.props.fallback ?? (
<div className="p-8 text-center">
<p>出错了。</p>
<button onClick={() => this.setState({ hasError: false })}>重试</button>
</div>
);
return this.props.children;
}
}
// app/dashboard/error.tsx
export default function Error({ error, reset }: { error: Error; reset: () => void }) {
useEffect(() => { Sentry.captureException(error); }, [error]);
return <div><p>此段加载失败。</p><button onClick={reset}>重试</button></div>;
}常见问题
- ⚠单一根错误边界,侧边栏 bug 杀主内容。
- ⚠用 console.error 而非服务记错误,prod 错误不可见。
- ⚠无'重试'按钮,用户唯一选项重载丢未存工作。
- ⚠捕错误不记,bug 隐藏永不修。
最佳实践
- ✓错误边界放路由段级非仅根——隔离失败到坏段。
- ✓UI 和日志含错误 ID,支持可关联用户报告到栈追踪。
- ✓Next.js error.tsx 用 React reset() 重试失败渲染无需全页重载。
- ✓