引言
在 React 应用中,一个组件的错误不应导致整个应用崩溃。错误边界(Error Boundaries)是 React 提供的错误隔离机制,它能捕获子组件树中的 JavaScript 错误,并显示降级 UI 而非让整个应用白屏。
什么是错误边界?
错误边界是类组件,通过实现 getDerivedStateFromError() 或 componentDidCatch() 生命周期方法来捕获子组件的错误。
class ErrorBoundary extends React.Component {
constructor(props) {
super(props);
this.state = { hasError: false, error: null };
}
static getDerivedStateFromError(error) {
// 渲染降级 UI
return { hasError: true };
}
componentDidCatch(error, errorInfo) {
// 记录错误日志
console.error('捕获错误:', error, errorInfo);
this.setState({ error });
}
render() {
if (this.state.hasError) {
return <FallbackUI error={this.state.error} />;
}
return this.props.children;
}
}
核心 API 对比
| 方法 | 调用时机 | 用途 | 能否访问组件实例 |
|---|---|---|---|
getDerivedStateFromError | 渲染阶段 | 更新 state 显示降级 UI | ❌ 静态方法 |
componentDidCatch | 提交阶段 | 记录错误日志、上报 | ✅ 可访问实例 |
实战:完整的错误边界组件
class ErrorBoundary extends React.Component {
constructor(props) {
super(props);
this.state = {
hasError: false,
error: null,
errorInfo: null
};
}
static getDerivedStateFromError(error) {
return { hasError: true };
}
componentDidCatch(error, errorInfo) {
this.setState({ error, errorInfo });
// 上报到错误监控平台
logErrorToService(error, errorInfo);
}
handleRetry = () => {
this.setState({ hasError: false, error: null, errorInfo: null });
};
render() {
if (this.state.hasError) {
return (
<div className="error-boundary">
<h2>😕 出错了</h2>
<p>组件渲染失败,请尝试刷新</p>
<button onClick={this.handleRetry}>重试</button>
{process.env.NODE_ENV === 'development' && (
<details>
<summary>错误详情</summary>
<pre>{this.state.error?.toString()}</pre>
</details>
)}
</div>
);
}
return this.props.children;
}
}
错误上报策略
// 错误上报服务
async function logErrorToService(error, errorInfo) {
const errorData = {
message: error.message,
stack: error.stack,
componentStack: errorInfo.componentStack,
url: window.location.href,
userAgent: navigator.userAgent,
timestamp: Date.now()
};
// 使用 sendBeacon 确保上报成功
navigator.sendBeacon('/api/log-error', JSON.stringify(errorData));
// 或发送到第三方监控平台
// Sentry.captureException(error, { contexts: { react: errorInfo } });
}
使用场景
// ✅ 包裹可能出错的子组件
<ErrorBoundary>
<UserProfile userId={123} />
</ErrorBoundary>
// ✅ 多个独立边界,隔离错误
<ErrorBoundary fallback={<ChatFallback />}>
<ChatWidget />
</ErrorBoundary>
<ErrorBoundary fallback={<FeedFallback />}>
<NewsFeed />
</ErrorBoundary>
// ❌ 不要包裹整个应用(失去隔离意义)
<ErrorBoundary>
<App />
</ErrorBoundary>
注意事项
| 能捕获 | 不能捕获 |
|---|---|
| 子组件渲染错误 | 事件处理器中的错误 |
| 生命周期错误 | 异步代码(setTimeout、requestAnimationFrame) |
| 构造函数错误 | SSR 服务端错误 |
| 边界组件自身的错误 |
总结
- 错误边界必须是类组件(Hooks 方案需用第三方库如 react-error-boundary)
- 精细化包裹:在可能出错的组件周围单独设置边界
- 优雅降级:提供友好的错误提示和重试机制
- 错误上报:记录错误信息用于后续分析修复
- 开发环境:显示详细错误信息便于调试