Skip to content
返回构建教程

错误边界

加错误边界和全局错误处理,崩溃显示恢复路径非白屏。

为什么

错误白屏是最差 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() 重试失败渲染无需全页重载。
  • ✓设 Sentry beforeSend 去日志前剥 PII(邮箱、密码)——永不记用户密钥。