React 性能监控与错误上报

发布于:2025-06-05 ⋅ 阅读:(22) ⋅ 点赞:(0)

核心问题与技术挑战

现代 React 应用随着业务复杂度增加,性能问题和运行时错误日益成为影响用户体验的关键因素。没有可靠的监控与错误上报机制,我们将陷入被动修复而非主动预防的困境。

性能指标体系与错误分类

关键性能指标定义

// performance-metrics.js
export const PERFORMANCE_METRICS = {
  // 页面加载指标
  FP: 'first-paint',                     // 首次绘制
  FCP: 'first-contentful-paint',         // 首次内容绘制
  LCP: 'largest-contentful-paint',       // 最大内容绘制
  FID: 'first-input-delay',              // 首次输入延迟
  TTI: 'time-to-interactive',            // 可交互时间
  TBT: 'total-blocking-time',            // 总阻塞时间
  CLS: 'cumulative-layout-shift',        // 累积布局偏移
  
  // React 特有指标
  COMPONENT_RENDER_TIME: 'component-render-time', // 组件渲染时间
  EFFECT_TIME: 'effect-execution-time',           // Effect执行时间
  RERENDER_COUNT: 'component-rerender-count',     // 组件重渲染次数
  CONTEXT_CHANGES: 'context-change-frequency',    // Context变更频率
  MEMO_HIT_RATE: 'memo-hit-rate',                 // memo命中率
};

错误分类与等级定义

// error-classification.js
export const ERROR_TYPES = {
  RENDER_ERROR: 'render-error',           // 渲染错误
  EFFECT_ERROR: 'effect-error',           // 副作用错误
  EVENT_HANDLER_ERROR: 'event-error',     // 事件处理错误
  ASYNC_ERROR: 'async-error',             // 异步操作错误
  RESOURCE_LOADING_ERROR: 'resource-error', // 资源加载错误
  API_ERROR: 'api-error',                 // API请求错误
  UNCAUGHT_ERROR: 'uncaught-error',       // 未捕获的错误
};

export const ERROR_LEVELS = {
  FATAL: 'fatal',       // 致命错误:导致应用崩溃或核心功能无法使用
  ERROR: 'error',       // 错误:功能无法正常工作,但不影响整体应用
  WARNING: 'warning',   // 警告:可能存在问题,但功能仍可使用
  INFO: 'info',         // 信息:值得注意的异常状况但无功能影响
};

监控工具选型与评估

Ran tool

监控工具对比分析

工具名称 类型 性能监控能力 错误捕获 集成复杂度 数据所有权 成本结构 适用场景
React Profiler 内置 中(组件级) 完全自有 免费 开发调试
Performance API 内置 完全自有 免费 核心指标采集
Sentry 第三方 外部存储 免费起步,按量付费 中小型应用
LogRocket 第三方 外部存储 付费 用户体验分析
自建系统 自研 可定制 可定制 完全自有 开发+维护成本 大型复杂应用

自定义监控系统架构设计

// 项目结构
/src
  /monitoring
    /performance
      metrics-collector.js
      render-tracker.js
      interaction-tracker.js
    /errors
      error-boundary.js
      error-handler.js
      api-error-tracker.js
    /reporting
      data-aggregator.js
      transport-layer.js
      batch-processor.js
    /config
      sampling-rules.js
      metrics-thresholds.js
    index.js

性能监控核心实现

性能数据采集器

// metrics-collector.js
import { PERFORMANCE_METRICS } from '../config/metrics-definitions';

class PerformanceMetricsCollector {
  metrics = new Map();
  markTimestamps = new Map();
  
  mark(name) {
    this.markTimestamps.set(name, performance.now());
    // 兼容 performance.mark API
    if (performance.mark) {
      performance.mark(`${name}-start`);
    }
  }
  
  measure(name, startMark) {
    if (!this.markTimestamps.has(startMark)) {
      console.warn(`Start mark "${startMark}" not found for measure "${name}"`);
      return;
    }
    
    const startTime = this.markTimestamps.get(startMark);
    const duration = performance.now() - startTime;
    
    // 记录此次测量值
    if (!this.metrics.has(name)) {
      this.metrics.set(name, []);
    }
    this.metrics.get(name).push(duration);
    
    // 兼容 performance.measure API
    if (performance.measure) {
      try {
        performance.measure(name, `${startMark}-start`);
      } catch (e) {
        // 某些浏览器在mark不存在时会抛出异常
      }
    }
    
    return duration;
  }
  
  getMetrics(name) {
    if (!this.metrics.has(name)) return null;
    const values = this.metrics.get(name);
    
    return {
      name,
      values,
      min: Math.min(...values),
      max: Math.max(...values),
      avg: values.reduce((sum, val) => sum + val, 0) / values.length,
      median: this.calculateMedian(values),
      p95: this.calculatePercentile(values, 95),
    };
  }
  
  getAllMetrics() {
    const result = {};
    this.metrics.forEach((values, name) => {
      result[name] = this.getMetrics(name);
    });
    return result;
  }
  
  calculateMedian(values) {
    if (!values.length) return 0;
    const sorted = [...values].sort((a, b) => a - b);
    const middle = Math.floor(sorted.length / 2);
    return sorted.length % 2 === 0
      ? (sorted[middle - 1] + sorted[middle]) / 2
      : sorted[middle];
  }
  
  calculatePercentile(values, percentile) {
    if (!values.length) return 0;
    const sorted = [...values].sort((a, b) => a - b);
    const index = Math.ceil((percentile / 100) * sorted.length) - 1;
    return sorted[index];
  }
  
  // Web Vitals指标采集
  captureWebVitals() {
    const { onLCP, onFID, onCLS, onTTFB } = require('web-vitals');
    
    onLCP(({ value }) => {
      if (!this.metrics.has(PERFORMANCE_METRICS.LCP)) {
        this.metrics.set(PERFORMANCE_METRICS.LCP, []);
      }
      this.metrics.get(PERFORMANCE_METRICS.LCP).push(value);
      this.reportMetric(PERFORMANCE_METRICS.LCP, value);
    });
    
    onFID(({ value }) => {
      if (!this.metrics.has(PERFORMANCE_METRICS.FID)) {
        this.metrics.set(PERFORMANCE_METRICS.FID, []);
      }
      this.metrics.get(PERFORMANCE_METRICS.FID).push(value);
      this.reportMetric(PERFORMANCE_METRICS.FID, value);
    });
    
    onCLS(({ value }) => {
      if (!this.metrics.has(PERFORMANCE_METRICS.CLS)) {
        this.metrics.set(PERFORMANCE_METRICS.CLS, []);
      }
      this.metrics.get(PERFORMANCE_METRICS.CLS).push(value);
      this.reportMetric(PERFORMANCE_METRICS.CLS, value);
    });
    
    onTTFB(({ value }) => {
      if (!this.metrics.has('TTFB')) {
        this.metrics.set('TTFB', []);
      }
      this.metrics.get('TTFB').push(value);
      this.reportMetric('TTFB', value);
    });
  }
  
  reportMetric(name, value) {
    // 将指标发送到上报系统
    if (this.reporter) {
      this.reporter.sendMetric({
        name,
        value,
        timestamp: Date.now()
      });
    }
  }
  
  setReporter(reporter) {
    this.reporter = reporter;
  }
}

export const metricsCollector = new PerformanceMetricsCollector();
export default metricsCollector;

React 组件性能追踪 HOC

// render-tracker.js
import React, { Component } from 'react';
import metricsCollector from './metrics-collector';
import { PERFORMANCE_METRICS } from '../config/metrics-definitions';

// 追踪组件渲染性能的高阶组件
export function withRenderTracking(WrappedComponent, options = {}) {
  const {
    trackProps = false,
    trackState = false,
    componentName = WrappedComponent.displayName || WrappedComponent.name || 'Component',
    threshold = 16, // 默认阈值16ms (60fps)
  } = options;
  
  return class RenderTracker extends Component {
    static displayName = `RenderTracker(${componentName})`;
    
    // 记录重渲染次数
    rerenderCount = 0;
    
    // 用于记录渲染前props和state
    prevProps = null;
    prevState = null;
    
    componentDidMount() {
      this.recordMounting();
    }
    
    shouldComponentUpdate(nextProps, nextState) {
      this.prevProps = this.props;
      this.prevState = this.state;
      return true;
    }
    
    componentDidUpdate() {
      this.rerenderCount++;
      this.recordRerender();
      
      if (trackProps && this.prevProps) {
        const changedProps = this.getChangedProps(this.prevProps, this.props);
        if (Object.keys(changedProps).length > 0) {
          this.recordPropChanges(changedProps);
        }
      }
      
      if (trackState && this.prevState) {
        const changedState = this.getChangedProps(this.prevState, this.state);
        if (Object.keys(changedState).length > 0) {
          this.recordStateChanges(changedState);
        }
      }
    }
    
    getChangedProps(prev, current) {
      const changes = {};
      Object.keys(current).forEach(key => {
        if (prev[key] !== current[key]) {
          changes[key] = {
            from: prev[key],
            to: current[key]
          };
        }
      });
      return changes;
    }
    
    recordMounting() {
      const renderTime = metricsCollector.measure(
        `${componentName}-mount`,
        `${componentName}-render-start`
      );
      
      if (renderTime > threshold) {
        console.warn(
          `[Performance] Component ${componentName} took ${renderTime.toFixed(2)}ms to mount, ` +
          `which exceeds the threshold of ${threshold}ms.`
        );
      }
      
      metricsCollector.reportMetric(
        PERFORMANCE_METRICS.COMPONENT_RENDER_TIME,
        { componentName, phase: 'mount', duration: renderTime }
      );
    }
    
    recordRerender() {
      const renderTime = metricsCollector.measure(
        `${componentName}-rerender-${this.rerenderCount}`,
        `${componentName}-render-start`
      );
      
      if (renderTime > threshold) {
        console.warn(
          `[Performance] Component ${componentName} took ${renderTime.toFixed(2)}ms to rerender, ` +
          `which exceeds the threshold of ${threshold}ms. (Rerender #${this.rerenderCount})`
        );
      }
      
      metricsCollector.reportMetric(
        PERFORMANCE_METRICS.COMPONENT_RENDER_TIME,
        { componentName, phase: 'update', count: this.rerenderCount, duration: renderTime }
      );
      
      metricsCollector.reportMetric(
        PERFORMANCE_METRICS.RERENDER_COUNT,
        { componentName, count: this.rerenderCount }
      );
    }
    
    recordPropChanges(changedProps) {
      metricsCollector.reportMetric(
        'prop-changes',
        { componentName, changes: changedProps }
      );
    }
    
    recordStateChanges(changedState) {
      metricsCollector.reportMetric(
        'state-changes',
        { componentName, changes: changedState }
      );
    }
    
    render() {
      metricsCollector.mark(`${componentName}-render-start`);
      return <WrappedComponent {...this.props} />;
    }
  };
}

// 针对函数组件的性能追踪Hook
export function useRenderTracking(componentName, options = {}) {
  const {
    threshold = 16
  } = options;
  
  const renderCount = React.useRef(0);
  
  React.useEffect(() => {
    const renderTime = metricsCollector.measure(
      `${componentName}-render-${renderCount.current}`,
      `${componentName}-render-start`
    );
    
    if (renderTime > threshold) {
      console.warn(
        `[Performance] Component ${componentName} took ${renderTime.toFixed(2)}ms to render, ` +
        `which exceeds the threshold of ${threshold}ms. (Render #${renderCount.current})`
      );
    }
    
    metricsCollector.reportMetric(
      PERFORMANCE_METRICS.COMPONENT_RENDER_TIME,
      { componentName, count: renderCount.current, duration: renderTime }
    );
    
    renderCount.current++;
  });
  
  // 在组件渲染之前标记
  React.useLayoutEffect(() => {
    metricsCollector.mark(`${componentName}-render-start`);
  }, [componentName]);
  
  return renderCount.current;
}

错误监控与上报系统

全局错误边界组件

// error-boundary.js
import React, { Component } from 'react';
import { ERROR_TYPES, ERROR_LEVELS } from '../config/error-classification';
import errorReporter from './error-reporter';

export class ErrorBoundary extends Component {
  static defaultProps = {
    fallback: null,
    onError: null,
    errorLevel: ERROR_LEVELS.ERROR,
    componentName: 'Unknown',
  };
  
  state = {
    hasError: false,
    error: null,
    errorInfo: null
  };
  
  componentDidCatch(error, errorInfo) {
    const { componentName, errorLevel, onError } = this.props;
    
    // 更新组件状态
    this.setState({
      hasError: true,
      error,
      errorInfo
    });
    
    // 获取组件树结构
    const componentStack = errorInfo?.componentStack || '';
    
    // 构造错误信息
    const errorData = {
      type: ERROR_TYPES.RENDER_ERROR,
      level: errorLevel,
      message: error.message,
      stack: error.stack,
      componentStack,
      componentName,
      time: Date.now(),
      url: window.location.href,
      userAgent: navigator.userAgent,
      // 错误的额外上下文
      context: {
        route: window.location.pathname,
        ...this.props.errorContext
      }
    };
    
    // 上报错误
    errorReporter.reportError(errorData);
    
    // 调用父组件错误处理函数
    if (typeof onError === 'function') {
      onError(error, errorInfo);
    }
    
    // 记录到控制台
    console.error('[ErrorBoundary]', error, errorInfo);
  }
  
  resetError = () => {
    this.setState({
      hasError: false,
      error: null,
      errorInfo: null
    });
  };
  
  render() {
    const { fallback, children } = this.props;
    const { hasError, error, errorInfo } = this.state;
    
    if (hasError) {
      // 提供重置错误的方法给fallback组件
      const resetHandler = {
        resetError: this.resetError
      };
      
      // 如果提供了fallback组件
      if (fallback) {
        if (React.isValidElement(fallback)) {
          return React.cloneElement(fallback, {
            error,
            errorInfo,
            ...resetHandler
          });
        } else if (typeof fallback === 'function') {
          return fallback({
            error,
            errorInfo,
            ...resetHandler
          });
        }
      }
      
      // 默认错误UI
      return (
        <div className="error-boundary-fallback">
          <h2>组件渲染出错</h2>
          <p>
            抱歉,组件渲染出现了问题。请尝试刷新页面或联系技术支持。
          </p>
          <button onClick={this.resetError}>重试</button>
        </div>
      );
    }
    
    return children;
  }
}

// 高阶组件封装
export function withErrorBoundary(Component, options = {}) {
  const {
    fallback,
    onError,
    errorLevel = ERROR_LEVELS.ERROR,
    errorContext = {}
  } = options;
  
  const componentName = Component.displayName || Component.name || 'Unknown';
  
  const WrappedComponent = (props) => (
    <ErrorBoundary
      fallback={fallback}
      onError={onError}
      errorLevel={errorLevel}
      componentName={componentName}
      errorContext={{
        ...errorContext,
        props: Object.keys(props)
      }}
    >
      <Component {...props} />
    </ErrorBoundary>
  );
  
  WrappedComponent.displayName = `withErrorBoundary(${componentName})`;
  return WrappedComponent;
}

全局错误捕获服务

// error-handler.js
import { ERROR_TYPES, ERROR_LEVELS } from '../config/error-classification';

class ErrorHandler {
  constructor(reporter) {
    this.reporter = reporter;
    this.initialized = false;
    this.ignorePatterns = [
      // 忽略一些非关键或第三方错误
      /Script error\./i,
      /ResizeObserver loop limit exceeded/i,
    ];
  }
  
  initialize() {
    if (this.initialized) return;
    
    // 捕获未处理的Promise异常
    window.addEventListener('unhandledrejection', this.handlePromiseRejection);
    
    // 捕获全局JavaScript错误
    window.addEventListener('error', this.handleWindowError, true);
    
    // 拦截console.error (可选)
    if (this.options?.interceptConsoleError) {
      this.originalConsoleError = console.error;
      console.error = (...args) => {
        this.handleConsoleError(...args);
        this.originalConsoleError.apply(console, args);
      };
    }
    
    this.initialized = true;
    
    console.log('[ErrorHandler] Global error handler initialized');
  }
  
  setOptions(options = {}) {
    this.options = {
      captureUnhandledRejections: true,
      captureGlobalErrors: true,
      interceptConsoleError: false,
      samplingRate: 1.0, // 1.0 = 捕获所有错误
      maxErrorsPerMinute: 10,
      ...options
    };
  }
  
  setReporter(reporter) {
    this.reporter = reporter;
  }
  
  handleWindowError = (event) => {
    // 阻止浏览器默认错误处理
    event.preventDefault();
    
    const { message, filename, lineno, colno, error } = event;
    
    // 检查是否应忽略此错误
    if (this.shouldIgnoreError(message)) {
      return true;
    }
    
    // 构造错误信息
    const errorData = {
      type: ERROR_TYPES.UNCAUGHT_ERROR,
      level: ERROR_LEVELS.FATAL,
      message,
      stack: error?.stack || '',
      source: {
        file: filename,
        line: lineno,
        column: colno
      },
      time: Date.now(),
      url: window.location.href,
      userAgent: navigator.userAgent
    };
    
    // 上报错误
    this.reportError(errorData);
    
    return true;
  };
  
  handlePromiseRejection = (event) => {
    // 组装有意义的错误信息
    const error = event.reason;
    const message = error?.message || 'Unhandled Promise Rejection';
    
    // 检查是否应忽略此错误
    if (this.shouldIgnoreError(message)) {
      return;
    }
    
    const errorData = {
      type: ERROR_TYPES.ASYNC_ERROR,
      level: ERROR_LEVELS.ERROR,
      message,
      stack: error?.stack || '',
      time: Date.now(),
      url: window.location.href,
      userAgent: navigator.userAgent
    };
    
    // 上报错误
    this.reportError(errorData);
  };
  
  handleConsoleError = (...args) => {
    // 提取有意义的错误信息
    const errorMessage = args.map(arg => 
      typeof arg === 'object' ? JSON.stringify(arg) : String(arg)
    ).join(' ');
    
    // 检查是否应忽略此错误
    if (this.shouldIgnoreError(errorMessage)) {
      return;
    }
    
    const errorData = {
      type: ERROR_TYPES.CONSOLE_ERROR,
      level: ERROR_LEVELS.WARNING,
      message: errorMessage.slice(0, 500), // 限制长度
      time: Date.now(),
      url: window.location.href
    };
    
    // 上报错误
    this.reportError(errorData);
  };
  
  shouldIgnoreError(message) {
    // 检查是否匹配忽略模式
    return this.ignorePatterns.some(pattern => pattern.test(message));
  }
  
  addIgnorePattern(pattern) {
    if (pattern instanceof RegExp) {
      this.ignorePatterns.push(pattern);
    } else if (typeof pattern === 'string') {
      this.ignorePatterns.push(new RegExp(pattern, 'i'));
    }
  }
  
  reportError(errorData) {
    // 采样控制
    if (Math.random() > this.options?.samplingRate) {
      return;
    }
    
    // 限流控制
    if (this.isRateLimited()) {
      return;
    }
    
    // 使用上报服务发送错误
    if (this.reporter) {
      this.reporter.sendError(errorData);
    }
  }
  
  // 实现错误上报频率限制
  isRateLimited() {
    const now = Date.now();
    const maxPerMinute = this.options?.maxErrorsPerMinute || 10;
    
    if (!this._errorTimestamps) {
      this._errorTimestamps = [];
    }
    
    // 清理一分钟前的错误记录
    this._errorTimestamps = this._errorTimestamps.filter(
      timestamp => now - timestamp < 60000
    );
    
    // 检查是否超出限制
    if (this._errorTimestamps.length >= maxPerMinute) {
      return true;
    }
    
    // 记录当前错误时间戳
    this._errorTimestamps.push(now);
    return false;
  }
  
  // 清理资源
  destroy() {
    if (!this.initialized) return;
    
    window.removeEventListener('unhandledrejection', this.handlePromiseRejection);
    window.removeEventListener('error', this.handleWindowError, true);
    
    if (this.originalConsoleError) {
      console.error = this.originalConsoleError;
    }
    
    this.initialized = false;
  }
}

export const errorHandler = new ErrorHandler();
export default errorHandler;

API 错误跟踪器

// api-error-tracker.js
import { ERROR_TYPES, ERROR_LEVELS } from '../config/error-classification';

// 创建拦截器以追踪API请求错误
export function createAPIErrorTracker(reporter) {
  // Fetch API拦截
  const originalFetch = window.fetch;
  
  window.fetch = async function trackedFetch(url, options = {}) {
    const startTime = performance.now();
    const requestId = generateRequestId();
    
    // 捕获请求信息
    const requestInfo = {
      url: typeof url === 'string' ? url : url.url,
      method: options.method || 'GET',
      requestId,
      startTime
    };
    
    try {
      // 记录请求开始
      reporter.sendMetric({
        name: 'api-request-start',
        value: requestInfo
      });
      
      // 执行原始请求
      const response = await originalFetch.apply(this, arguments);
      
      // 计算请求时间
      const duration = performance.now() - startTime;
      
      // 处理非2xx响应
      if (!response.ok) {
        let responseBody = '';
        try {
          // 克隆响应以便仍可读取主体
          const clonedResponse = response.clone();
          responseBody = await clonedResponse.text();
        } catch (e) {
          // 无法读取响应体
          responseBody = 'Unable to read response body';
        }
        
        // 上报API错误
        const errorData = {
          type: ERROR_TYPES.API_ERROR,
          level: response.status >= 500 ? ERROR_LEVELS.ERROR : ERROR_LEVELS.WARNING,
          message: `API Error: ${response.status} ${response.statusText}`,
          time: Date.now(),
          url: window.location.href,
          context: {
            request: {
              url: requestInfo.url,
              method: requestInfo.method,
              requestId
            },
            response: {
              status: response.status,
              statusText: response.statusText,
              body: responseBody.substring(0, 1000) // 限制大小
            },
            duration
          }
        };
        
        reporter.sendError(errorData);
      }
      
      // 记录请求完成
      reporter.sendMetric({
        name: 'api-request-complete',
        value: {
          ...requestInfo,
          status: response.status,
          duration,
          success: response.ok
        }
      });
      
      return response;
    } catch (error) {
      // 计算请求时间
      const duration = performance.now() - startTime;
      
      // 上报网络错误
      const errorData = {
        type: ERROR_TYPES.API_ERROR,
        level: ERROR_LEVELS.ERROR,
        message: `Network Error: ${error.message}`,
        stack: error.stack,
        time: Date.now(),
        url: window.location.href,
        context: {
          request: {
            url: requestInfo.url,
            method: requestInfo.method,
            requestId
          },
          error: error.message,
          duration
        }
      };
      
      reporter.sendError(errorData);
      
      // 记录请求失败
      reporter.sendMetric({
        name: 'api-request-failed',
        value: {
          ...requestInfo,
          error: error.message,
          duration,
          success: false
        }
      });
      
      // 重新抛出原始错误
      throw error;
    }
  };
  
  // Axios拦截器(如果项目使用Axios)
  if (window.axios) {
    window.axios.interceptors.request.use(config => {
      config.requestId = generateRequestId();
      config.startTime = performance.now();
      
      // 记录请求开始
      reporter.sendMetric({
        name: 'api-request-start',
        value: {
          url: config.url,
          method: config.method,
          requestId: config.requestId,
          startTime: config.startTime
        }
      });
      
      return config;
    });
    
    window.axios.interceptors.response.use(
      response => {
        const { config } = response;
        const duration = performance.now() - config.startTime;
        
        // 记录请求完成
        reporter.sendMetric({
          name: 'api-request-complete',
          value: {
            url: config.url,
            method: config.method,
            requestId: config.requestId,
            status: response.status,
            duration,
            success: true
          }
        });
        
        return response;
      },
      error => {
        const { config, response } = error;
        
        // 某些情况下请求可能未发出
        if (!config) {
          return Promise.reject(error);
        }
        
        const duration = performance.now() - config.startTime;
        
        // 上报API错误
        const errorData = {
          type: ERROR_TYPES.API_ERROR,
          level: response?.status >= 500 ? ERROR_LEVELS.ERROR : ERROR_LEVELS.WARNING,
          message: `API Error: ${response?.status || 'Network Error'} ${error.message}`,
          time: Date.now(),
          url: window.location.href,
          context: {
            request: {
              url: config.url,
              method: config.method,
              requestId: config.requestId
            },
            response: response ? {
              status: response.status,
              statusText: response.statusText,
              data: response.data
            } : null,
            duration
          }
        };
        
        reporter.sendError(errorData);
        
        // 记录请求失败
        reporter.sendMetric({
          name: 'api-request-failed',
          value: {
            url: config.url,
            method: config.method,
            requestId: config.requestId,
            error: error.message,
            status: response?.status,
            duration,
            success: false
          }
        });
        
        return Promise.reject(error);
      }
    );
  }
  
  // 生成请求ID
  function generateRequestId() {
    return `req_${Date.now()}_${Math.random().toString(36).substring(2, 10)}`;
  }
  
  return {
    // 恢复原始方法
    restore: () => {
      window.fetch = originalFetch;
      if (window.axios) {
        window.axios.interceptors.request.eject(0);
        window.axios.interceptors.response.eject(0);
      }
    }
  };
}

数据上报与聚合系统

上报传输层

// transport-layer.js
class DataTransport {
  constructor(options = {}) {
    this.options = {
      endpoint: '/monitoring/collect',
      batchSize: 10,
      flushInterval: 5000, // 5秒
      retryAttempts: 3,
      retryDelay: 1000,
      useBeacon: true,
      ...options
    };
    
    this.buffer = [];
    this.isSending = false;
    this.flushTimer = null;
    this.retryQueue = [];
    
    // 启动定期刷新
    this.startPeriodicFlush();
    
    // 页面卸载时发送所有待处理数据
    window.addEventListener('beforeunload', this.flushBeforeUnload);
  }
  
  setEndpoint(endpoint) {
    this.options.endpoint = endpoint;
  }
  
  send(data) {
    // 添加通用字段
    const enrichedData = {
      ...data,
      timestamp: data.timestamp || Date.now(),
      session: this.getSessionInfo(),
      user: this.getUserInfo(),
      app: this.getAppInfo()
    };
    
    // 添加到缓冲区
    this.buffer.push(enrichedData);
    
    // 如果达到批处理大小,立即发送
    if (this.buffer.length >= this.options.batchSize) {
      this.flush();
    }
    
    return true;
  }
  
  async flush() {
    if (this.isSending || this.buffer.length === 0) {
      return;
    }
    
    this.isSending = true;
    
    // 提取当前缓冲区数据
    const dataToSend = [...this.buffer];
    this.buffer = [];
    
    try {
      // 尝试发送数据
      const success = await this.sendData(dataToSend);
      
      if (!success) {
        // 如果发送失败,将数据添加到重试队列
        this.addToRetryQueue(dataToSend);
      }
    } catch (error) {
      console.error('[Monitoring] Error sending monitoring data:', error);
      // 发送失败,添加到重试队列
      this.addToRetryQueue(dataToSend);
    }
    
    this.isSending = false;
    
    // 处理重试队列
    if (this.retryQueue.length > 0) {
      this.processRetryQueue();
    }
  }
  
  async sendData(data) {
    // 检查页面是否正在卸载
    if (this.isUnloading && this.options.useBeacon && navigator.sendBeacon) {
      // 使用Beacon API发送数据(更可靠地处理页面卸载场景)
      const blob = new Blob([JSON.stringify(data)], { type: 'application/json' });
      return navigator.sendBeacon(this.options.endpoint, blob);
    } else {
      // 使用标准fetch API
      try {
        const response = await fetch(this.options.endpoint, {
          method: 'POST',
          headers: {
            'Content-Type': 'application/json',
          },
          body: JSON.stringify(data),
          // 不跟随重定向
          redirect: 'error',
          // 发送凭据(例如cookies)
          credentials: 'same-origin',
          // 设置较短的超时时间
          signal: AbortSignal.timeout(10000) // 10秒超时
        });
        
        return response.ok;
      } catch (error) {
        console.error('[Monitoring] Transport error:', error);
        return false;
      }
    }
  }
  
  addToRetryQueue(data) {
    // 添加到重试队列,并记录重试次数
    this.retryQueue.push({
      data,
      attempts: 0,
      nextRetry: Date.now() + this.options.retryDelay
    });
  }
  
  async processRetryQueue() {
    if (this.isProcessingRetryQueue) {
      return;
    }
    
    this.isProcessingRetryQueue = true;
    
    const now = Date.now();
    const itemsToRetry = this.retryQueue.filter(item => item.nextRetry <= now);
    
    // 更新重试队列,删除将要重试的项目
    this.retryQueue = this.retryQueue.filter(item => item.nextRetry > now);
    
    for (const item of itemsToRetry) {
      // 增加重试次数
      item.attempts++;
      
      try {
        const success = await this.sendData(item.data);
        
        if (!success && item.attempts < this.options.retryAttempts) {
          // 计算下一次重试时间(使用指数退避)
          const nextRetryDelay = this.options.retryDelay * Math.pow(2, item.attempts - 1);
          item.nextRetry = Date.now() + nextRetryDelay;
          
          // 重新添加到队列
          this.retryQueue.push(item);
        }
      } catch (error) {
        if (item.attempts < this.options.retryAttempts) {
          // 计算下一次重试时间
          const nextRetryDelay = this.options.retryDelay * Math.pow(2, item.attempts - 1);
          item.nextRetry = Date.now() + nextRetryDelay;
          
          // 重新添加到队列
          this.retryQueue.push(item);
        }
      }
    }
    
    this.isProcessingRetryQueue = false;
  }
  
  startPeriodicFlush() {
    // 定期刷新缓冲区
    this.flushTimer = setInterval(() => {
      if (this.buffer.length > 0) {
        this.flush();
      }
      
      // 处理重试队列
      if (this.retryQueue.length > 0) {
        this.processRetryQueue();
      }
    }, this.options.flushInterval);
  }
  
  flushBeforeUnload = () => {
    // 标记正在卸载,这会使sendData使用navigator.beacon
    this.isUnloading = true;
    
    // 取消计时器
    clearInterval(this.flushTimer);
    
    // 合并重试队列和当前缓冲区
    const allPendingData = [
      ...this.buffer,
      ...this.retryQueue.map(item => item.data).flat()
    ];
    
    if (allPendingData.length > 0) {
      // 使用同步方式发送所有数据
      const blob = new Blob([JSON.stringify(allPendingData)], { type: 'application/json' });
      navigator.sendBeacon(this.options.endpoint, blob);
    }
  };
  
  getSessionInfo() {
    // 在实际应用中,应该从会话管理系统获取这些信息
    return {
      id: window.sessionStorage.getItem('session_id') || 'unknown',
      startedAt: parseInt(window.sessionStorage.getItem('session_start') || Date.now()),
      pageViews: parseInt(window.sessionStorage.getItem('page_views') || '1')
    };
  }
  
  getUserInfo() {
    // 在实际应用中,应该从身份验证系统获取这些信息
    // 注意:确保遵守隐私法规和公司政策
    return {
      // 使用匿名ID或经过同意的用户标识符
      id: window.localStorage.getItem('user_id') || 'anonymous',
      // 可以添加经过许可的用户属性
      type: window.localStorage.getItem('user_type') || 'visitor'
    };
  }
  
  getAppInfo() {
    return {
      name: window.APP_NAME || document.title,
      version: window.APP_VERSION || '1.0',
      environment: window.APP_ENV || process.env.NODE_ENV || 'production',
      reactVersion: React.version,
      viewportSize: `${window.innerWidth}x${window.innerHeight}`,
      language: navigator.language,
      platform: navigator.platform
    };
  }
  
  destroy() {
    // 清理资源
    clearInterval(this.flushTimer);
    window.removeEventListener('beforeunload', this.flushBeforeUnload);
    
    // 发送所有待处理数据
    if (this.buffer.length > 0 || this.retryQueue.length > 0) {
      this.flushBeforeUnload();
    }
  }
}

export const dataTransport = new DataTransport();
export default dataTransport;

数据聚合与批处理器

// batch-processor.js
import dataTransport from './transport-layer';

class MonitoringReporter {
  constructor(transport) {
    this.transport = transport;
    this.metricsBuffer = {};
    this.errorBuffer = [];
    this.flushInterval = 10000; // 10秒
    this.bufferSize = {
      metrics: 20,
      errors: 5
    };
    
    // 启动定期批处理
    this.startPeriodicFlush();
  }
  
  // 发送性能指标
  sendMetric(metric) {
    const { name, value } = metric;
    
    // 按指标类型进行分组
    if (!this.metricsBuffer[name]) {
      this.metricsBuffer[name] = [];
    }
    
    // 添加时间戳
    const metricWithTimestamp = {
      ...metric,
      timestamp: metric.timestamp || Date.now()
    };
    
    // 添加到缓冲区
    this.metricsBuffer[name].push(metricWithTimestamp);
    
    // 如果该类型的指标达到阈值,就发送此类型的所有指标
    if (this.metricsBuffer[name].length >= this.bufferSize.metrics) {
      this.flushMetricsByType(name);
    }
    
    return true;
  }
  
  // 发送错误
  sendError(error) {
    // 添加到错误缓冲区
    this.errorBuffer.push({
      ...error,
      timestamp: error.timestamp || Date.now()
    });
    
    // 如果错误达到阈值,立即发送
    if (this.errorBuffer.length >= this.bufferSize.errors) {
      this.flushErrors();
    }
    
    return true;
  }
  
  // 按指标类型刷新缓冲区
  flushMetricsByType(metricType) {
    if (!this.metricsBuffer[metricType] || this.metricsBuffer[metricType].length === 0) {
      return;
    }
    
    // 提取要发送的指标
    const metricsToSend = [...this.metricsBuffer[metricType]];
    
    // 清空缓冲区
    this.metricsBuffer[metricType] = [];
    
    // 构造批量数据包
    const payload = {
      type: 'metric',
      metricType,
      data: metricsToSend
    };
    
    // 发送到传输层
    this.transport.send(payload);
  }
  
  // 刷新所有错误
  flushErrors() {
    if (this.errorBuffer.length === 0) {
      return;
    }
    
    // 提取要发送的错误
    const errorsToSend = [...this.errorBuffer];
    
    // 清空缓冲区
    this.errorBuffer = [];
    
    // 构造批量数据包
    const payload = {
      type: 'error',
      data: errorsToSend
    };
    
    // 发送到传输层
    this.transport.send(payload);
  }
  
  // 刷新所有指标
  flushAllMetrics() {
    // 遍历所有指标类型
    Object.keys(this.metricsBuffer).forEach(metricType => {
      if (this.metricsBuffer[metricType].length > 0) {
        this.flushMetricsByType(metricType);
      }
    });
  }
  
  // 刷新所有数据
  flushAll() {
    this.flushAllMetrics();
    this.flushErrors();
  }
  
  // 启动定期刷新
  startPeriodicFlush() {
    this.flushTimer = setInterval(() => {
      this.flushAll();
    }, this.flushInterval);
    
    // 页面隐藏时刷新数据
    document.addEventListener('visibilitychange', () => {
      if (document.visibilityState === 'hidden') {
        this.flushAll();
      }
    });
  }
  
  // 设置缓冲区大小
  setBufferSize(type, size) {
    if (type === 'metrics' || type === 'errors') {
      this.bufferSize[type] = size;
    }
  }
  
  // 销毁实例,清理资源
  destroy() {
    clearInterval(this.flushTimer);
    this.flushAll();
  }
}

export const reporter = new MonitoringReporter(dataTransport);
export default reporter;

系统集成与自动化配置

监控系统初始化

// index.js
import React from 'react';
import { PERFORMANCE_METRICS } from './config/metrics-definitions';
import { ERROR_LEVELS } from './config/error-classification';
import metricsCollector from './performance/metrics-collector';
import { withRenderTracking, useRenderTracking } from './performance/render-tracker';
import { ErrorBoundary, withErrorBoundary } from './errors/error-boundary';
import errorHandler from './errors/error-handler';
import { createAPIErrorTracker } from './errors/api-error-tracker';
import reporter from './reporting/batch-processor';
import dataTransport from './reporting/transport-layer';

// 默认配置
const defaultConfig = {
  enablePerformanceMonitoring: true,
  enableErrorMonitoring: true,
  errorReportingEndpoint: '/api/error-reporting',
  metricsReportingEndpoint: '/api/metrics-reporting',
  samplingRate: 0.1, // 采样 10% 的用户
  logLevel: ERROR_LEVELS.ERROR, // 仅报告错误及以上级别
  maxErrorsPerMinute: 10,
  captureConsoleErrors: false,
  enableReactProfiling: false,
};

// 监控系统主类
class ReactMonitoring {
  constructor() {
    this.initialized = false;
    this.config = { ...defaultConfig };
  }
  
  init(userConfig = {}) {
    if (this.initialized) {
      console.warn('[ReactMonitoring] Already initialized. Call destroy() first if you need to reinitialize.');
      return this;
    }
    
    // 合并用户配置
    this.config = {
      ...defaultConfig,
      ...userConfig,
    };
    
    // 随机采样决定是否为这个用户启用监控
    const shouldMonitor = Math.random() < this.config.samplingRate;
    if (!shouldMonitor) {
      console.log('[ReactMonitoring] This session was not selected for monitoring (sampling)');
      return this;
    }
    
    // 配置数据传输层
    dataTransport.setEndpoint(this.config.errorReportingEndpoint);
    
    // 初始化错误处理
    if (this.config.enableErrorMonitoring) {
      this.initErrorMonitoring();
    }
    
    // 初始化性能监控
    if (this.config.enablePerformanceMonitoring) {
      this.initPerformanceMonitoring();
    }
    
    this.initialized = true;
    console.log('[ReactMonitoring] Initialized successfully');
    
    return this;
  }
  
  initErrorMonitoring() {
    // 配置错误处理器
    errorHandler.setOptions({
      captureUnhandledRejections: true,
      captureGlobalErrors: true,
      interceptConsoleError: this.config.captureConsoleErrors,
      samplingRate: 1.0, // 捕获所有发生的错误
      maxErrorsPerMinute: this.config.maxErrorsPerMinute,
    });
    
    // 设置错误上报服务
    errorHandler.setReporter(reporter);
    
    // 初始化全局错误处理
    errorHandler.initialize();
    
    // 创建API错误跟踪器
    this.apiErrorTracker = createAPIErrorTracker(reporter);
    
    console.log('[ReactMonitoring] Error monitoring initialized');
  }
  
  initPerformanceMonitoring() {
    // 配置指标收集器
    metricsCollector.setReporter(reporter);
    
    // 捕获Web Vitals
    metricsCollector.captureWebVitals();
    
    // 捕获首次加载性能
    this.captureInitialPerformance();
    
    if (this.config.enableReactProfiling && window.__REACT_DEVTOOLS_GLOBAL_HOOK__) {
      this.setupReactProfiling();
    }
    
    console.log('[ReactMonitoring] Performance monitoring initialized');
  }
  
  captureInitialPerformance() {
    // 利用Performance Timeline API捕获关键性能指标
    if (window.performance && performance.timing) {
      // 等待加载完成
      if (document.readyState === 'complete') {
        this.processPerformanceTiming();
      } else {
        window.addEventListener('load', () => {
          // 延迟一下以确保所有指标都已可用
          setTimeout(() => this.processPerformanceTiming(), 0);
        });
      }
    }
  }
  
  processPerformanceTiming() {
    const timing = performance.timing;
    
    // 计算关键性能指标
    const metrics = {
      // DNS解析时间
      dns: timing.domainLookupEnd - timing.domainLookupStart,
      // TCP连接时间
      tcp: timing.connectEnd - timing.connectStart,
      // 请求时间
      request: timing.responseStart - timing.requestStart,
      // 响应时间
      response: timing.responseEnd - timing.responseStart,
      // DOM解析时间
      domParse: timing.domInteractive - timing.responseEnd,
      // DOM内容加载
      domContentLoaded: timing.domContentLoadedEventEnd - timing.domContentLoadedEventStart,
      // DOM完全加载
      domComplete: timing.domComplete - timing.domLoading,
      // 页面完全加载时间
      pageLoad: timing.loadEventEnd - timing.navigationStart,
      // 首次渲染时间(近似)
      firstPaint: timing.domLoading - timing.navigationStart,
    };
    
    // 上报指标
    Object.entries(metrics).forEach(([name, value]) => {
      reporter.sendMetric({
        name: `page_${name}`,
        value,
        category: 'page-load',
      });
    });
  }
  
  setupReactProfiling() {
    // 这需要React DevTools扩展的钩子
    const hook = window.__REACT_DEVTOOLS_GLOBAL_HOOK__;
    
    if (hook && hook.supportsFiber) {
      // 获取React实例
      const renderers = hook.getFiberRoots ? hook.getFiberRoots(1) : null;
      
      if (renderers) {
        for (const renderer of renderers) {
          // 添加分析器
          if (hook.onCommitFiberRoot) {
            const originalOnCommitFiberRoot = hook.onCommitFiberRoot.bind(hook);
            
            hook.onCommitFiberRoot = (...args) => {
              const [, root] = args;
              
              try {
                this.processReactCommit(root);
              } catch (e) {
                console.error('[ReactMonitoring] Error in React profiling:', e);
              }
              
              // 调用原始方法
              return originalOnCommitFiberRoot(...args);
            };
          }
        }
      }
    }
  }
  
  processReactCommit(root) {
    // 这是一个简化版的实现
    // 实际上,从Fiber树提取性能数据很复杂,需要深入了解React内部工作原理
    try {
      const commitTime = performance.now();
      
      reporter.sendMetric({
        name: PERFORMANCE_METRICS.COMPONENT_RENDER_TIME,
        value: {
          commitTime,
          components: this.extractComponentInfo(root)
        }
      });
    } catch (e) {
      console.error('[ReactMonitoring] Failed to process React commit:', e);
    }
  }
  
  extractComponentInfo(root) {
    // 这是一个简化的实现
    // 在实际应用中,需要遍历Fiber树来提取组件信息
    return {
      timestamp: performance.now(),
      // 这里应该有更多组件特定的数据
    };
  }
  
  // 提供React组件和钩子
  getReactComponents() {
    return {
      ErrorBoundary,
      withErrorBoundary,
      withRenderTracking,
      useRenderTracking,
    };
  }
  
  // 清理和销毁监控系统
  destroy() {
    if (!this.initialized) {
      return;
    }
    
    // 清理错误处理
    if (errorHandler) {
      errorHandler.destroy();
    }
    
    // 清理API错误跟踪
    if (this.apiErrorTracker) {
      this.apiErrorTracker.restore();
    }
    
    // 清理数据上报
    if (reporter) {
      reporter.destroy();
    }
    
    if (dataTransport) {
      dataTransport.destroy();
    }
    
    this.initialized = false;
    console.log('[ReactMonitoring] System destroyed and cleaned up');
  }
}

// 创建单例实例
export const reactMonitoring = new ReactMonitoring();

// 导出React组件和钩子,方便直接使用
export const {
  ErrorBoundary,
  withErrorBoundary,
  withRenderTracking,
  useRenderTracking,
} = reactMonitoring.getReactComponents();

// 默认导出监控系统实例
export default reactMonitoring;

应用实践

应用集成示例

// 在应用入口 index.js 中初始化
import React from 'react';
import ReactDOM from 'react-dom';
import App from './App';
import reactMonitoring from './monitoring';

// 初始化监控系统
reactMonitoring.init({
  enablePerformanceMonitoring: true,
  enableErrorMonitoring: true,
  errorReportingEndpoint: 'https://api.example.com/monitoring/errors',
  metricsReportingEndpoint: 'https://api.example.com/monitoring/metrics',
  samplingRate: process.env.NODE_ENV === 'production' ? 0.1 : 1.0, // 生产环境采样10%,开发环境全采样
});

// 使用全局错误边界包装应用
const MonitoredApp = () => (
  <reactMonitoring.ErrorBoundary
    fallback={<div>应用出现了问题,正在尝试恢复...</div>}
    errorLevel="fatal"
  >
    <App />
  </reactMonitoring.ErrorBoundary>
);

ReactDOM.render(<MonitoredApp />, document.getElementById('root'));

组件级性能监控示例

// 使用HOC监控类组件
import React, { Component } from 'react';
import { withRenderTracking, withErrorBoundary } from './monitoring';

class ExpensiveComponent extends Component {
  render() {
    return (
      <div>
        {/* 复杂组件逻辑 */}
        {this.props.items.map(item => (
          <div key={item.id}>{item.name}</div>
        ))}
      </div>
    );
  }
}

// 应用监控HOC
export default withErrorBoundary(
  withRenderTracking(ExpensiveComponent, {
    componentName: 'ExpensiveComponent',
    threshold: 8, // 8ms渲染警告阈值
    trackProps: true
  }), 
  {
    fallback: <div>组件加载失败</div>,
    errorLevel: 'error'
  }
);

// 使用Hook监控函数组件
import React, { useState } from 'react';
import { useRenderTracking } from './monitoring';

function ExpensiveCounter(props) {
  // 监控组件渲染性能
  const renderCount = useRenderTracking('ExpensiveCounter', { threshold: 5 });
  
  const [count, setCount] = useState(0);
  
  // 模拟一个可能导致性能问题的操作
  const expensiveCalculation = () => {
    // 假设这是一个昂贵的计算
    let result = 0;
    for (let i = 0; i < 1000000; i++) {
      result += i;
    }
    return result;
  };
  
  const result = expensiveCalculation();
  
  return (
    <div>
      <p>Count: {count}</p>
      <p>Calculation: {result}</p>
      <p>Render count: {renderCount}</p>
      <button onClick={() => setCount(count + 1)}>Increment</button>
    </div>
  );
}

export default ExpensiveCounter;

监控数据可视化方案

// 监控仪表板组件
import React, { useState, useEffect } from 'react';
import { LineChart, BarChart, PieChart } from 'some-chart-library';
import { fetchMetricsData, fetchErrorData } from '../api';

export function PerformanceDashboard() {
  const [metrics, setMetrics] = useState(null);
  const [errors, setErrors] = useState(null);
  const [timeRange, setTimeRange] = useState('24h');
  const [loading, setLoading] = useState(true);
  
  useEffect(() => {
    async function loadData() {
      setLoading(true);
      try {
        // 并行加载数据
        const [metricsData, errorsData] = await Promise.all([
          fetchMetricsData({ timeRange }),
          fetchErrorData({ timeRange })
        ]);
        
        setMetrics(metricsData);
        setErrors(errorsData);
      } catch (err) {
        console.error('Failed to load monitoring data:', err);
      } finally {
        setLoading(false);
      }
    }
    
    loadData();
  }, [timeRange]);
  
  if (loading) {
    return <div>Loading dashboard data...</div>;
  }
  
  // 渲染性能指标图表
  return (
    <div className="monitoring-dashboard">
      <div className="dashboard-header">
        <h1>React Application Monitoring</h1>
        <div className="time-selector">
          <button onClick={() => setTimeRange('1h')}>Last Hour</button>
          <button onClick={() => setTimeRange('24h')}>Last 24 Hours</button>
          <button onClick={() => setTimeRange('7d')}>Last 7 Days</button>
        </div>
      </div>
      
      <div className="dashboard-section">
        <h2>Core Web Vitals</h2>
        <div className="metrics-grid">
          <MetricCard
            title="LCP"
            value={metrics.lcp.median}
            threshold={2500}
            unit="ms"
            description="Largest Contentful Paint"
          />
          <MetricCard
            title="FID"
            value={metrics.fid.median}
            threshold={100}
            unit="ms"
            description="First Input Delay"
          />
          <MetricCard
            title="CLS"
            value={metrics.cls.median}
            threshold={0.1}
            unit=""
            description="Cumulative Layout Shift"
          />
        </div>
        
        <h3>LCP Trend</h3>
        <LineChart
          data={metrics.lcp.history}
          xKey="timestamp"
          yKey="value"
          threshold={2500}
        />
      </div>
      
      <div className="dashboard-section">
        <h2>Component Performance</h2>
        <BarChart
          data={metrics.componentRenderTime}
          xKey="componentName"
          yKey="avgDuration"
          sortBy="avgDuration"
        />
      </div>
      
      <div className="dashboard-section">
        <h2>Error Distribution</h2>
        <PieChart
          data={errors.byType}
          valueKey="count"
          labelKey="type"
        />
        
        <h3>Recent Errors</h3>
        <ErrorsTable errors={errors.recent} />
      </div>
    </div>
  );
}

// 单个指标卡片组件
function MetricCard({ title, value, threshold, unit, description }) {
  // 根据阈值确定状态
  const getStatus = () => {
    if (value <= threshold * 0.75) return 'good';
    if (value <= threshold) return 'warning';
    return 'poor';
  };
  
  const status = getStatus();
  
  return (
    <div className={`metric-card ${status}`}>
      <div className="metric-title">{title}</div>
      <div className="metric-value">
        {value.toFixed(2)}{unit}
      </div>
      <div className="metric-description">{description}</div>
      <div className="metric-threshold">
        {status === 'good' && '✓ Good'}
        {status === 'warning' && '⚠️ Needs Improvement'}
        {status === 'poor' && '✗ Poor'}
      </div>
    </div>
  );
}

// 错误表格组件
function ErrorsTable({ errors }) {
  return (
    <table className="errors-table">
      <thead>
        <tr>
          <th>Time</th>
          <th>Type</th>
          <th>Message</th>
          <th>Component</th>
          <th>Actions</th>
        </tr>
      </thead>
      <tbody>
        {errors.map(error => (
          <tr key={error.id}>
            <td>{new Date(error.timestamp).toLocaleString()}</td>
            <td>{error.type}</td>
            <td>{error.message}</td>
            <td>{error.componentName || 'N/A'}</td>
            <td>
              <button onClick={() => viewErrorDetails(error.id)}>Details</button>
            </td>
          </tr>
        ))}
      </tbody>
    </table>
  );
}

性能优化建议与实施方案

基于监控收集的数据,我们可以制定针对性的优化策略:

  1. 组件懒加载与代码分割:根据页面加载性能数据,识别首屏加载瓶颈,将非关键组件延迟加载。

  2. 状态管理优化:利用渲染追踪数据,识别过度渲染的组件,应用 React.memouseMemouseCallback 降低不必要的重渲染。

  3. 虚拟化长列表:对于识别出渲染时间过长的列表组件,应用窗口化技术(react-window)仅渲染可视区域项目。

  4. 图片与资源优化:根据资源加载错误和性能数据,优化静态资源加载策略,应用懒加载与适当的分辨率。

  5. 错误预防机制:基于收集的错误模式构建更健壮的输入验证和错误恢复机制,提高应用稳定性。

总结与反思

构建完整的 React 性能监控与错误上报系统需要系统性地考虑数据采集、传输、存储和分析等环节。我们应该遵循以下原则:

  1. 低侵入性:通过 HOC 和 Hooks 模式,实现了对组件的无痛监控,不影响业务逻辑。

  2. 可扩展性:模块化设计使系统易于根据实际需求进行扩展和定制。

  3. 性能影响最小化:采样策略和批处理机制确保监控系统本身不会成为应用的性能负担。

  4. 数据安全与隐私:提供了匿名化和数据过滤机制,符合现代数据保护要求。

  5. 自动化分析:通过阈值检测和趋势分析,实现了问题的自动识别与预警。

在实际应用中,还应根据项目规模和需求选择合适的集成方式,从简单的单一指标监控开始,逐步扩展到全面的性能与错误追踪系统,以持续提升 React 应用的质量与用户体验。

参考资源

官方文档与规范

监控工具与框架

  • Sentry - 功能丰富的错误监控平台,提供 React SDK
  • LogRocket - 会话重放和前端监控系统
  • New Relic - 全栈性能监控解决方案
  • Datadog RUM - 真实用户监控平台

开源库

服务端集成

技术博客与最佳实践

行业标准与测量工具


如果你觉得这篇文章有帮助,欢迎点赞收藏,也期待在评论区看到你的想法和建议!👇

终身学习,共同成长。

咱们下一期见

💻


网站公告

今日签到

点亮在社区的每一天
去签到