跳到主要内容

前端容错与降级策略

场景

线上业务需要保证高可用,即使部分功能出错也不能让整个页面崩溃。如何设计前端的容错和降级方案?

方案设计

1. 组件级容错:ErrorBoundary

ErrorBoundary.tsx
import { Component, type ReactNode, type ErrorInfo } from 'react';

interface Props {
children: ReactNode;
fallback?: ReactNode;
onError?: (error: Error, errorInfo: ErrorInfo) => void;
}

interface State {
hasError: boolean;
}

class ErrorBoundary extends Component<Props, State> {
state: State = { hasError: false };

static getDerivedStateFromError(): State {
return { hasError: true };
}

componentDidCatch(error: Error, errorInfo: ErrorInfo) {
this.props.onError?.(error, errorInfo);
// 上报错误监控
reportError({ error, componentStack: errorInfo.componentStack });
}

render() {
if (this.state.hasError) {
return this.props.fallback ?? <div>模块加载失败,请刷新重试</div>;
}
return this.props.children;
}
}

// 使用方式:每个独立模块包裹
function App() {
return (
<div>
<ErrorBoundary fallback={<div>头部加载失败</div>}>
<Header />
</ErrorBoundary>
<ErrorBoundary fallback={<div>内容区加载失败</div>}>
<MainContent />
</ErrorBoundary>
</div>
);
}

2. 接口降级

api-fallback.ts
interface FallbackConfig<T> {
primary: () => Promise<T>;
fallback: () => Promise<T> | T;
cache?: () => T | null;
}

async function withFallback<T>(config: FallbackConfig<T>): Promise<T> {
try {
const result = await config.primary();
return result;
} catch (error) {
console.warn('Primary API failed, trying fallback:', error);

// 先尝试读缓存
if (config.cache) {
const cached = config.cache();
if (cached !== null) return cached;
}

// 再尝试降级接口
return config.fallback();
}
}

// 使用示例
const data = await withFallback({
primary: () => fetch('/api/v2/data').then((r) => r.json()),
fallback: () => fetch('/api/v1/data').then((r) => r.json()),
cache: () => JSON.parse(localStorage.getItem('data_cache') ?? 'null'),
});

3. 功能降级策略

降级级别策略示例
L0 全量降级切换到静态页CDN 全挂时展示静态 HTML
L1 模块降级隐藏/替换模块推荐模块出错时隐藏
L2 功能降级简化功能实时搜索降级为提交搜索
L3 体验降级移除非核心关闭动画、埋点

常见面试问题

Q1: 前端如何实现灰度/Feature Flag 降级?

答案

feature-flag.ts
const flags = await fetch('/api/feature-flags').then((r) => r.json());

function isFeatureEnabled(key: string): boolean {
return flags[key] ?? false;
}

// 组件中使用
function SearchBox() {
if (isFeatureEnabled('ai_search')) {
return <AISearch />;
}
return <BasicSearch />; // 降级到基础搜索
}

通过后端控制 Feature Flag,可以随时关闭有问题的功能,无需发版。

Q2: ErrorBoundary 能捕获哪些错误?不能捕获哪些?

答案

  • 能捕获:子组件渲染时的 JS 错误、生命周期方法中的错误
  • 不能捕获:事件处理器中的错误、异步代码(setTimeout/Promise)、SSR 错误、ErrorBoundary 自身的错误

事件处理器中的错误需要用 try-catch 或全局 window.onerror 捕获。

相关链接