人人都会AI编程

10.5 自定义指令:全局注册、局部注册、生命周期钩子、典型应用场景

更新时间:2026-07-10

在复杂的 React 应用中,任何组件都可能因未预见的异常而崩溃。如果不加处理,一个组件内部的错误会像雪崩一样导致整个组件树卸载,给用户呈现一片空白的界面。错误边界(Error Boundary)正是 React 提供的一种专门机制,用于捕获子组件树中发生的异常,并展示降级 UI,避免整个应用白屏

10.5.1 错误边界是什么

错误边界是一个 React 组件,它通过实现 componentDidCatchgetDerivedStateFromError 两个生命周期方法(或其中之一),来捕获其子组件在渲染阶段、生命周期方法和构造函数中抛出的错误。错误边界无法捕获以下类型的错误:

  • 事件处理函数中的错误(这部分需要用 try/catch 手动处理)
  • 异步代码中的错误(如 setTimeoutPromise 回调中的异常)
  • 服务端渲染期间的错误
  • 错误边界自身抛出的错误

也就是说,错误边界主要针对渲染路径上的同步异常,确保 UI 不会整体崩溃。

10.5.2 实现一个错误边界

传统类组件可以通过生命周期方法实现错误边界。下面是一个典型的示例:

class ErrorBoundary extends React.Component {
  constructor(props) {
    super(props);
    this.state = { hasError: false, error: null };
  }

  static getDerivedStateFromError(error) {
    // 更新 state,触发下一次渲染展示备用 UI
    return { hasError: true, error };
  }

  componentDidCatch(error, errorInfo) {
    // 可以在这里将错误上报给服务器
    console.error('ErrorBoundary caught:', error, errorInfo);
    // 例如:reportErrorToService(error, errorInfo);
  }

  render() {
    if (this.state.hasError) {
      // 降级 UI,允许自定义 fallback
      return this.props.fallback || (
        <div style={{ padding: 20, textAlign: 'center' }}>
          <h2>出现了意外错误</h2>
          <p>请刷新页面重试</p>
        </div>
      );
    }

    return this.props.children;
  }
}

核心方法说明:

  • static getDerivedStateFromError(error):静态方法,在子组件抛出错误后被调用,返回的对象会合并到 state 中。它应当只用来设置错误状态,不包含副作用(如网络请求)。
  • componentDidCatch(error, errorInfo):同样在错误发生后调用,但允许执行副作用,比如把错误日志发送到监控系统。errorInfo.componentStack 可以告诉你错误发生在哪个组件栈中。

使用时,只需用 ErrorBoundary 包裹可能出错的组件树:

function App() {
  return (
    <ErrorBoundary fallback={<FallbackUI />}>
      <Sidebar />
      <MainContent />
    </ErrorBoundary>
  );
}

10.5.3 在函数组件中使用错误边界

React Hooks 目前没有提供与类组件的 componentDidCatch 完全等价的官方 Hook。但我们可以通过包装类组件,或使用第三方库(如 react-error-boundary)来获得函数式的使用体验。

推荐:react-error-boundary

这个库提供了 ErrorBoundary 组件和 useErrorBoundary Hook,完全兼容函数组件生态:

npm install react-error-boundary

使用示例:

import { ErrorBoundary } from 'react-error-boundary';

function ErrorFallback({ error, resetErrorBoundary }) {
  return (
    <div role="alert">
      <p>加载失败:{error.message}</p>
      <button onClick={resetErrorBoundary}>重试</button>
    </div>
  );
}

function MyComponent() {
  // 组件可能抛出错误
  if (Math.random() > 0.7) {
    throw new Error('随机错误');
  }
  return <div>正常内容</div>;
}

function App() {
  return (
    <ErrorBoundary
      FallbackComponent={ErrorFallback}
      onReset={() => {
        // 重置逻辑,比如清空状态重新加载数据
      }}
    >
      <MyComponent />
    </ErrorBoundary>
  );
}

react-error-boundary 还支持 onError 回调用于日志上报,以及 resetKeys 属性自动重置错误边界,非常实用。

10.5.4 错误边界的实际应用场景

1. 模块级隔离

在大型应用中,不应只用一个顶层的错误边界包裹整个应用。更好的实践是对关键模块分别包裹错误边界,这样右侧栏崩溃不影响左侧内容区,功能卡片报错也不会让整个页面不可用。

<Layout>
  <ErrorBoundary>
    <Sidebar />
  </ErrorBoundary>
  <ErrorBoundary>
    <Content />
  </ErrorBoundary>
</Layout>

2. 路由级保护

对于由路由驱动的应用,可以在每个路由组件外部包裹错误边界,当某个页面加载失败时只影响当前页面,其他路由仍然可用。React Router 的数据路由(Loader/Action)抛出的错误也可以通过错误边界统一处理。

3. 第三方组件隔离

当使用不可控的第三方组件时,它们可能因为特定数据或版本问题抛错。用错误边界包裹这些组件,可以保证即使第三方库出问题,也不拖垮整个应用。

4. 优雅降级与重试

结合重置功能,错误边界可以给用户“重试”选项,重新挂载组件树,尝试恢复正常。这对于网络波动导致的瞬时错误尤为有效。

10.5.5 注意事项与常见坑点

  • 不能捕获事件处理中的错误:如果你的按钮点击处理函数中写错了属性访问,错误边界不会捕获。你需要在事件处理内部使用 try/catch 或者将错误通过状态提升为渲染异常(例如 throw 在 render 中)。
  • 不能捕获异步错误useEffectsetTimeout 中的错误不会触发错误边界。对于异步操作,一定要在 Promise 链中 .catch(),或者使用 try/catch 包裹 await 代码,并将错误设置为状态,以便触发错误边界。
  • 捕获后组件树会完全卸载:当错误边界捕获到错误后,其子组件树会被完全卸载,并在降级 UI 的位置重新渲染。如果错误的子组件持有重要的状态,状态将会丢失。因此,考虑通过 resetKeyskey 来强制重新挂载。
  • 错误边界自身不能有异常:错误边界必须是一个健壮的组件,其自身的 rendergetDerivedStateFromErrorcomponentDidCatch 都不应抛出错误,否则错误会向上冒泡到更外层的错误边界,甚至直接导致整个应用崩溃。
  • 生产与开发环境差异:在开发模式下,React 会在错误边界捕获后仍然显示红色错误遮罩和调用栈,方便调试;而生产环境才会真正显示降级 UI。这可能会让你误以为错误边界没有生效,实际只是调试特性。

错误边界是构建健壮 React 应用的必备防线。合理划分边界层级,结合日志上报和用户友好的降级提示,可以显著提升应用在异常情况下的用户体验和可维护性。