Skip to content

常见性能问题与解决 - React性能问题诊断手册

1. 渲染性能问题

1.1 不必要的重渲染

typescript
const unnecessaryRerender = {
  问题1_父组件导致: `
    function Parent() {
      const [count, setCount] = useState(0);
      
      return (
        <div>
          <button onClick={() => setCount(c => c + 1)}>
            {count}
          </button>
          <ExpensiveChild />  {/* count变化时也重渲染 */}
        </div>
      );
    }
    
    function ExpensiveChild() {
      console.log('ExpensiveChild render');
      return <div>{heavyComputation()}</div>;
    }
  `,
  
  解决方案: `
    // 方案1: 使用React.memo
    const ExpensiveChild = React.memo(function ExpensiveChild() {
      console.log('ExpensiveChild render');
      return <div>{heavyComputation()}</div>;
    });
    
    // 方案2: 状态下放
    function Parent() {
      return (
        <div>
          <Counter />
          <ExpensiveChild />
        </div>
      );
    }
    
    function Counter() {
      const [count, setCount] = useState(0);
      return <button onClick={() => setCount(c => c + 1)}>{count}</button>;
    }
    
    // 方案3: children prop
    function Parent({ children }) {
      const [count, setCount] = useState(0);
      
      return (
        <div>
          <button onClick={() => setCount(c => c + 1)}>{count}</button>
          {children}  {/* children不会重新创建 */}
        </div>
      );
    }
    
    <Parent>
      <ExpensiveChild />
    </Parent>
  `,
  
  问题2_内联对象: `
    function Parent() {
      return (
        <Child 
          style={{ color: 'red' }}  {/* 每次都是新对象 */}
          config={{ theme: 'dark' }}  {/* 每次都是新对象 */}
        />
      );
    }
    
    const Child = React.memo(({ style, config }) => {
      console.log('Child render');  // 每次都渲染
      return <div style={style}>{config.theme}</div>;
    });
  `,
  
  解决: `
    // 提取到组件外
    const style = { color: 'red' };
    const config = { theme: 'dark' };
    
    function Parent() {
      return <Child style={style} config={config} />;
    }
    
    // 或使用useMemo
    function Parent() {
      const style = useMemo(() => ({ color: 'red' }), []);
      const config = useMemo(() => ({ theme: 'dark' }), []);
      
      return <Child style={style} config={config} />;
    }
  `
};

1.2 大列表性能问题

typescript
const largeListProblems = {
  问题: `
    function List({ items }) {  // items有10000项
      return (
        <ul>
          {items.map(item => (
            <li key={item.id}>
              <div>{item.name}</div>
              <div>{item.description}</div>
              <button onClick={() => handleClick(item.id)}>Action</button>
            </li>
          ))}
        </ul>
      );
    }
    
    // 问题:
    // - 渲染10000个DOM节点
    // - 初始渲染慢
    // - 滚动卡顿
    // - 内存占用大
  `,
  
  解决方案1_虚拟滚动: `
    import { FixedSizeList } from 'react-window';
    
    function VirtualList({ items }) {
      const Row = ({ index, style }) => {
        const item = items[index];
        return (
          <div style={style}>
            <div>{item.name}</div>
            <div>{item.description}</div>
            <button onClick={() => handleClick(item.id)}>Action</button>
          </div>
        );
      };
      
      return (
        <FixedSizeList
          height={600}
          itemCount={items.length}
          itemSize={80}
          width="100%"
        >
          {Row}
        </FixedSizeList>
      );
    }
    
    // 性能提升:
    // - 只渲染可见区域(约10-15项)
    // - 滚动流畅
    // - 内存占用低
  `,
  
  解决方案2_分页: `
    function PaginatedList({ items }) {
      const [page, setPage] = useState(1);
      const pageSize = 20;
      
      const paginatedItems = useMemo(() => {
        const start = (page - 1) * pageSize;
        return items.slice(start, start + pageSize);
      }, [items, page]);
      
      return (
        <div>
          <ul>
            {paginatedItems.map(item => (
              <ListItem key={item.id} item={item} />
            ))}
          </ul>
          
          <Pagination
            page={page}
            total={Math.ceil(items.length / pageSize)}
            onChange={setPage}
          />
        </div>
      );
    }
  `,
  
  解决方案3_无限滚动: `
    function InfiniteList() {
      const [items, setItems] = useState([]);
      const [page, setPage] = useState(1);
      const [hasMore, setHasMore] = useState(true);
      
      const loadMore = useCallback(async () => {
        const newItems = await fetchItems(page);
        setItems(prev => [...prev, ...newItems]);
        setPage(p => p + 1);
        setHasMore(newItems.length > 0);
      }, [page]);
      
      return (
        <InfiniteScroll
          dataLength={items.length}
          next={loadMore}
          hasMore={hasMore}
          loader={<Loading />}
        >
          {items.map(item => (
            <Item key={item.id} item={item} />
          ))}
        </InfiniteScroll>
      );
    }
  `
};

1.3 Context导致的重渲染

typescript
const contextRerender = {
  问题: `
    const AppContext = createContext();
    
    function App() {
      const [user, setUser] = useState(null);
      const [theme, setTheme] = useState('light');
      const [count, setCount] = useState(0);
      
      // ❌ value每次都是新对象
      const value = { user, setUser, theme, setTheme, count, setCount };
      
      return (
        <AppContext.Provider value={value}>
          <Page />
        </AppContext.Provider>
      );
    }
    
    function Page() {
      const { theme } = useContext(AppContext);
      // count变化也会导致Page重渲染,即使只用了theme
      return <div className={theme}>Content</div>;
    }
  `,
  
  解决方案1_分离Context: `
    const UserContext = createContext();
    const ThemeContext = createContext();
    const CountContext = createContext();
    
    function App() {
      const [user, setUser] = useState(null);
      const [theme, setTheme] = useState('light');
      const [count, setCount] = useState(0);
      
      return (
        <UserContext.Provider value={{ user, setUser }}>
          <ThemeContext.Provider value={{ theme, setTheme }}>
            <CountContext.Provider value={{ count, setCount }}>
              <Page />
            </CountContext.Provider>
          </ThemeContext.Provider>
        </UserContext.Provider>
      );
    }
    
    function Page() {
      const { theme } = useContext(ThemeContext);
      // 只有theme变化才重渲染
      return <div className={theme}>Content</div>;
    }
  `,
  
  解决方案2_useMemo: `
    function App() {
      const [user, setUser] = useState(null);
      const [theme, setTheme] = useState('light');
      
      const value = useMemo(
        () => ({ user, setUser, theme, setTheme }),
        [user, theme]
      );
      
      return (
        <AppContext.Provider value={value}>
          <Page />
        </AppContext.Provider>
      );
    }
  `,
  
  解决方案3_状态管理库: `
    // 使用Zustand
    const useStore = create((set) => ({
      user: null,
      theme: 'light',
      count: 0,
      setUser: (user) => set({ user }),
      setTheme: (theme) => set({ theme }),
      setCount: (count) => set({ count })
    }));
    
    function Page() {
      const theme = useStore(state => state.theme);
      // 只订阅theme,其他状态变化不影响
      return <div className={theme}>Content</div>;
    }
  `
};

2. 内存泄漏问题

2.1 事件监听器未清理

typescript
const eventListenerLeak = {
  问题: `
    function Component() {
      useEffect(() => {
        const handler = () => console.log('resize');
        window.addEventListener('resize', handler);
        
        // ❌ 忘记清理
      }, []);
      
      return <div>Component</div>;
    }
    
    // 组件卸载后,监听器仍然存在
  `,
  
  解决: `
    function Component() {
      useEffect(() => {
        const handler = () => console.log('resize');
        window.addEventListener('resize', handler);
        
        // ✓ 清理监听器
        return () => {
          window.removeEventListener('resize', handler);
        };
      }, []);
      
      return <div>Component</div>;
    }
  `
};

2.2 定时器未清理

typescript
const timerLeak = {
  问题: `
    function Component() {
      useEffect(() => {
        const timer = setInterval(() => {
          console.log('tick');
        }, 1000);
        
        // ❌ 未清理定时器
      }, []);
    }
  `,
  
  解决: `
    function Component() {
      useEffect(() => {
        const timer = setInterval(() => {
          console.log('tick');
        }, 1000);
        
        // ✓ 清理定时器
        return () => clearInterval(timer);
      }, []);
    }
  `
};

2.3 异步操作未取消

typescript
const asyncLeak = {
  问题: `
    function Component({ id }) {
      const [data, setData] = useState(null);
      
      useEffect(() => {
        fetchData(id).then(result => {
          setData(result);  // 组件卸载后仍然执行
        });
      }, [id]);
    }
  `,
  
  解决: `
    function Component({ id }) {
      const [data, setData] = useState(null);
      
      useEffect(() => {
        let cancelled = false;
        
        fetchData(id).then(result => {
          if (!cancelled) {
            setData(result);
          }
        });
        
        return () => {
          cancelled = true;
        };
      }, [id]);
    }
    
    // 或使用AbortController
    useEffect(() => {
      const controller = new AbortController();
      
      fetch(url, { signal: controller.signal })
        .then(res => res.json())
        .then(setData)
        .catch(err => {
          if (err.name !== 'AbortError') {
            console.error(err);
          }
        });
      
      return () => controller.abort();
    }, [url]);
  `
};

3. 状态更新问题

3.1 批量更新失效

typescript
const batchingIssues = {
  React17问题: `
    // React 17: setTimeout中不批量
    function Component() {
      const [count, setCount] = useState(0);
      const [flag, setFlag] = useState(false);
      
      const handleClick = () => {
        setTimeout(() => {
          setCount(c => c + 1);  // 触发渲染1
          setFlag(f => !f);       // 触发渲染2
        }, 0);
      };
      
      // 触发2次渲染
    }
  `,
  
  React17解决: `
    import { unstable_batchedUpdates } from 'react-dom';
    
    const handleClick = () => {
      setTimeout(() => {
        unstable_batchedUpdates(() => {
          setCount(c => c + 1);
          setFlag(f => !f);
        });
      }, 0);
      
      // 只触发1次渲染
    };
  `,
  
  React18自动: `
    // React 18: 自动批量所有更新
    const handleClick = () => {
      setTimeout(() => {
        setCount(c => c + 1);
        setFlag(f => !f);
      }, 0);
      
      // 自动批量,只触发1次渲染
    };
  `
};

3.2 闭包陷阱

typescript
const closureTrap = {
  问题: `
    function Component() {
      const [count, setCount] = useState(0);
      
      useEffect(() => {
        const timer = setInterval(() => {
          setCount(count + 1);  // count永远是0
        }, 1000);
        
        return () => clearInterval(timer);
      }, []);  // 空依赖,闭包捕获初始count
      
      // count只会变成1,不会继续增加
    }
  `,
  
  解决: `
    // 方案1: 函数式更新
    useEffect(() => {
      const timer = setInterval(() => {
        setCount(c => c + 1);  // 基于最新值
      }, 1000);
      
      return () => clearInterval(timer);
    }, []);
    
    // 方案2: 添加依赖
    useEffect(() => {
      const timer = setInterval(() => {
        setCount(count + 1);
      }, 1000);
      
      return () => clearInterval(timer);
    }, [count]);  // count变化时重建定时器
    
    // 方案3: useRef
    const countRef = useRef(count);
    countRef.current = count;
    
    useEffect(() => {
      const timer = setInterval(() => {
        setCount(countRef.current + 1);
      }, 1000);
      
      return () => clearInterval(timer);
    }, []);
  `
};

4. 网络性能问题

4.1 Bundle过大

typescript
const bundleSize = {
  诊断: `
    // webpack-bundle-analyzer
    npm install --save-dev webpack-bundle-analyzer
    
    // package.json
    "analyze": "webpack-bundle-analyzer dist/stats.json"
  `,
  
  常见原因: [
    '导入整个库(如lodash)',
    '未使用的依赖',
    '重复打包',
    '未压缩',
    '未tree shaking'
  ],
  
  优化: `
    // 1. 按需导入
    // ❌ import _ from 'lodash';
    // ✓ import debounce from 'lodash/debounce';
    
    // 2. 代码分割
    const Heavy = lazy(() => import('./Heavy'));
    
    // 3. 动态导入
    const loadModule = async () => {
      const mod = await import('./module');
      return mod.default;
    };
    
    // 4. 替换大型库
    // moment.js (200KB) -> day.js (2KB)
    // lodash -> lodash-es (支持tree shaking)
  `
};

4.2 过多HTTP请求

typescript
const tooManyRequests = {
  问题: `
    function Component() {
      const [user, setUser] = useState(null);
      const [posts, setPosts] = useState([]);
      const [comments, setComments] = useState([]);
      
      useEffect(() => {
        fetchUser().then(setUser);      // 请求1
        fetchPosts().then(setPosts);    // 请求2
        fetchComments().then(setComments); // 请求3
      }, []);
      
      // 串行请求,慢
    }
  `,
  
  解决_并行请求: `
    useEffect(() => {
      Promise.all([
        fetchUser(),
        fetchPosts(),
        fetchComments()
      ]).then(([user, posts, comments]) => {
        setUser(user);
        setPosts(posts);
        setComments(comments);
      });
    }, []);
  `,
  
  解决_GraphQL: `
    // 一次请求获取所有数据
    const query = gql\`
      query PageData {
        user { id name }
        posts { id title }
        comments { id content }
      }
    \`;
    
    const { data } = useQuery(query);
  `,
  
  解决_BFF: `
    // Backend For Frontend
    // 后端聚合数据
    const data = await fetch('/api/page-data');
    // 一次请求返回所有数据
  `
};

5. 诊断工具

5.1 React DevTools Profiler

typescript
const profilerUsage = {
  记录性能: `
    1. 打开React DevTools
    2. 切换到Profiler标签
    3. 点击录制按钮
    4. 执行操作
    5. 停止录制
    6. 分析结果
  `,
  
  分析指标: {
    渲染时长: '组件渲染花费的时间',
    渲染原因: 'Why did this render?',
    提交时长: 'commit阶段花费的时间',
    组件树: '查看组件渲染层级'
  },
  
  找出问题: [
    '渲染时间过长的组件',
    '频繁重渲染的组件',
    '不必要的props更新',
    '昂贵的计算'
  ]
};

5.2 Chrome DevTools

typescript
const chromeDevTools = {
  Performance面板: `
    1. 打开DevTools
    2. 切换到Performance
    3. 录制
    4. 执行操作
    5. 停止
    6. 分析火焰图
  `,
  
  查看: [
    'JavaScript执行时间',
    '渲染时间',
    '布局计算',
    '重绘次数',
    '长任务(Long Tasks)'
  ],
  
  优化建议: [
    '减少JavaScript执行时间',
    '避免强制同步布局',
    '减少重绘重排',
    '拆分长任务'
  ]
};

6. 面试高频问题

typescript
const interviewQA = {
  Q1: {
    question: 'React性能问题常见原因?',
    answer: [
      '1. 不必要的重渲染',
      '2. 大列表渲染',
      '3. Bundle过大',
      '4. 内存泄漏',
      '5. Context滥用',
      '6. 未优化的图片',
      '7. 过多HTTP请求'
    ]
  },
  
  Q2: {
    question: '如何诊断性能问题?',
    answer: [
      '1. React DevTools Profiler',
      '2. Chrome DevTools Performance',
      '3. Lighthouse审计',
      '4. Web Vitals监控',
      '5. Bundle分析器',
      '6. 性能监控工具(Sentry等)'
    ]
  },
  
  Q3: {
    question: '如何避免不必要的重渲染?',
    answer: [
      '1. 使用React.memo',
      '2. 使用useMemo/useCallback',
      '3. 状态下放',
      '4. 组件拆分',
      '5. children prop模式',
      '6. 避免内联对象/函数'
    ]
  },
  
  Q4: {
    question: '内存泄漏如何排查和修复?',
    answer: [
      '1. Chrome DevTools Memory面板',
      '2. 检查事件监听器',
      '3. 检查定时器',
      '4. 检查异步操作',
      '5. useEffect返回清理函数',
      '6. 取消未完成的请求'
    ]
  }
};

7. 总结

常见性能问题与解决的核心要点:

  1. 重渲染: memo/useMemo/useCallback/组件拆分
  2. 大列表: 虚拟滚动/分页/无限滚动
  3. Context: 分离/useMemo/状态管理库
  4. 内存泄漏: 清理监听器/定时器/异步
  5. Bundle: 代码分割/tree shaking/按需导入
  6. 诊断: DevTools/Profiler/Lighthouse

掌握这些解决方案可以有效解决React性能问题。

8. 高级性能优化技巧

8.1 组件懒加载策略

jsx
// 基础懒加载
const HeavyComponent = lazy(() => import('./HeavyComponent'));

// 预加载策略
const HeavyComponent = lazy(() => import('./HeavyComponent'));

// 预加载函数
const preload = () => {
  const component = import('./HeavyComponent');
  return component;
};

// 在适当时机预加载
function App() {
  return (
    <div 
      onMouseEnter={preload}  // 鼠标悬停时预加载
    >
      <Link to="/heavy">进入页面</Link>
    </div>
  );
}

// 带超时的懒加载
function lazyWithTimeout(importFn, timeout = 5000) {
  return lazy(() => {
    return Promise.race([
      importFn(),
      new Promise((_, reject) => 
        setTimeout(() => reject(new Error('Timeout')), timeout)
      )
    ]);
  });
}

const TimeoutComponent = lazyWithTimeout(
  () => import('./SlowComponent'),
  3000
);

8.2 智能缓存策略

jsx
// LRU缓存实现
class LRUCache {
  constructor(capacity) {
    this.capacity = capacity;
    this.cache = new Map();
  }
  
  get(key) {
    if (!this.cache.has(key)) return null;
    const value = this.cache.get(key);
    // 移到最新位置
    this.cache.delete(key);
    this.cache.set(key, value);
    return value;
  }
  
  set(key, value) {
    if (this.cache.has(key)) {
      this.cache.delete(key);
    } else if (this.cache.size >= this.capacity) {
      // 删除最旧的
      const firstKey = this.cache.keys().next().value;
      this.cache.delete(firstKey);
    }
    this.cache.set(key, value);
  }
}

// React中使用LRU缓存
function useDataCache(capacity = 10) {
  const cache = useRef(new LRUCache(capacity));
  
  const fetchWithCache = useCallback(async (key, fetchFn) => {
    const cached = cache.current.get(key);
    if (cached) return cached;
    
    const data = await fetchFn();
    cache.current.set(key, data);
    return data;
  }, []);
  
  return fetchWithCache;
}

// 使用示例
function DataComponent({ userId }) {
  const [data, setData] = useState(null);
  const fetchWithCache = useDataCache(20);
  
  useEffect(() => {
    fetchWithCache(userId, () => fetchUserData(userId))
      .then(setData);
  }, [userId, fetchWithCache]);
  
  return <div>{data?.name}</div>;
}

8.3 Web Worker优化

jsx
// worker.js
self.addEventListener('message', (e) => {
  const { type, data } = e.data;
  
  if (type === 'PROCESS_DATA') {
    // 昂贵的计算
    const result = processLargeData(data);
    self.postMessage({ type: 'RESULT', data: result });
  }
});

function processLargeData(data) {
  // 复杂计算逻辑
  return data.map(item => {
    // 耗时操作
    return complexCalculation(item);
  });
}

// React组件使用Worker
function useWorker(workerPath) {
  const workerRef = useRef(null);
  
  useEffect(() => {
    workerRef.current = new Worker(workerPath);
    
    return () => {
      workerRef.current?.terminate();
    };
  }, [workerPath]);
  
  const postMessage = useCallback((message) => {
    return new Promise((resolve) => {
      const handleMessage = (e) => {
        workerRef.current.removeEventListener('message', handleMessage);
        resolve(e.data);
      };
      
      workerRef.current.addEventListener('message', handleMessage);
      workerRef.current.postMessage(message);
    });
  }, []);
  
  return postMessage;
}

// 使用示例
function DataProcessor({ data }) {
  const [result, setResult] = useState(null);
  const postMessage = useWorker('/worker.js');
  
  useEffect(() => {
    postMessage({ type: 'PROCESS_DATA', data })
      .then(response => setResult(response.data));
  }, [data, postMessage]);
  
  return <div>{result}</div>;
}

8.4 图片优化策略

jsx
// 渐进式图片加载
function ProgressiveImage({ lowQualitySrc, highQualitySrc, alt }) {
  const [src, setSrc] = useState(lowQualitySrc);
  const [loading, setLoading] = useState(true);
  
  useEffect(() => {
    const img = new Image();
    img.src = highQualitySrc;
    img.onload = () => {
      setSrc(highQualitySrc);
      setLoading(false);
    };
  }, [highQualitySrc]);
  
  return (
    <img
      src={src}
      alt={alt}
      style={{
        filter: loading ? 'blur(10px)' : 'none',
        transition: 'filter 0.3s'
      }}
    />
  );
}

// 懒加载图片
function LazyImage({ src, alt, placeholder }) {
  const [imageSrc, setImageSrc] = useState(placeholder);
  const [imageRef, setImageRef] = useState();
  
  useEffect(() => {
    let observer;
    
    if (imageRef && imageSrc === placeholder) {
      observer = new IntersectionObserver(
        entries => {
          entries.forEach(entry => {
            if (entry.isIntersecting) {
              setImageSrc(src);
              observer.unobserve(imageRef);
            }
          });
        },
        { rootMargin: '100px' }
      );
      
      observer.observe(imageRef);
    }
    
    return () => {
      if (observer && imageRef) {
        observer.unobserve(imageRef);
      }
    };
  }, [src, imageSrc, imageRef, placeholder]);
  
  return <img ref={setImageRef} src={imageSrc} alt={alt} />;
}

// WebP支持检测
function useWebPSupport() {
  const [supportsWebP, setSupportsWebP] = useState(false);
  
  useEffect(() => {
    const img = new Image();
    img.onload = () => setSupportsWebP(img.width === 1);
    img.onerror = () => setSupportsWebP(false);
    img.src = 'data:image/webp;base64,UklGRiQAAABXRUJQVlA4IBgAAAAwAQCdASoBAAEAAwA0JaQAA3AA/vuUAAA=';
  }, []);
  
  return supportsWebP;
}

// 智能图片组件
function SmartImage({ src, alt }) {
  const supportsWebP = useWebPSupport();
  const imageSrc = supportsWebP 
    ? src.replace(/\.(jpg|png)$/, '.webp') 
    : src;
  
  return <LazyImage src={imageSrc} alt={alt} placeholder="/placeholder.jpg" />;
}

9. 实战优化案例

9.1 电商产品列表优化

jsx
// 问题:10000个产品,严重卡顿

// 解决方案1:虚拟滚动 + 图片懒加载
import { FixedSizeList } from 'react-window';

function ProductList({ products }) {
  const Row = useCallback(({ index, style }) => {
    const product = products[index];
    return (
      <div style={style}>
        <ProductCard product={product} />
      </div>
    );
  }, [products]);
  
  return (
    <FixedSizeList
      height={600}
      itemCount={products.length}
      itemSize={120}
      width="100%"
    >
      {Row}
    </FixedSizeList>
  );
}

const ProductCard = memo(({ product }) => {
  return (
    <div className="product-card">
      <LazyImage src={product.image} alt={product.name} />
      <h3>{product.name}</h3>
      <p>{product.price}</p>
    </div>
  );
});

// 性能提升:
// - 首次渲染:从3000ms降到150ms
// - 滚动FPS:从15提升到60
// - 内存占用:从800MB降到120MB

9.2 复杂表单优化

jsx
// 问题:100个字段的表单,输入延迟严重

// 解决方案:字段级更新 + 虚拟化
function ComplexForm({ initialData, onSubmit }) {
  const [formData, setFormData] = useState(initialData);
  
  // 字段级更新,避免整个表单重渲染
  const updateField = useCallback((fieldName, value) => {
    setFormData(prev => ({
      ...prev,
      [fieldName]: value
    }));
  }, []);
  
  return (
    <form onSubmit={onSubmit}>
      {Object.keys(formData).map(fieldName => (
        <FormField
          key={fieldName}
          name={fieldName}
          value={formData[fieldName]}
          onChange={updateField}
        />
      ))}
    </form>
  );
}

const FormField = memo(({ name, value, onChange }) => {
  // 防抖输入
  const debouncedOnChange = useMemo(
    () => debounce((name, value) => onChange(name, value), 300),
    [onChange]
  );
  
  const handleChange = (e) => {
    debouncedOnChange(name, e.target.value);
  };
  
  return (
    <div>
      <label>{name}</label>
      <input 
        defaultValue={value}
        onChange={handleChange}
      />
    </div>
  );
});

// 性能提升:
// - 输入延迟:从200ms降到16ms
// - 每次输入重渲染次数:从100降到1

9.3 实时数据看板优化

jsx
// 问题:实时更新的数据看板,每秒100次更新

// 解决方案:批量更新 + 选择性渲染
function Dashboard() {
  const [metrics, setMetrics] = useState({});
  
  useEffect(() => {
    const ws = new WebSocket('wss://api.example.com');
    const buffer = [];
    let timeoutId;
    
    ws.onmessage = (event) => {
      const data = JSON.parse(event.data);
      buffer.push(data);
      
      // 批量更新,每50ms合并一次
      clearTimeout(timeoutId);
      timeoutId = setTimeout(() => {
        setMetrics(prev => {
          const newMetrics = { ...prev };
          buffer.forEach(item => {
            newMetrics[item.id] = item;
          });
          buffer.length = 0;
          return newMetrics;
        });
      }, 50);
    };
    
    return () => {
      clearTimeout(timeoutId);
      ws.close();
    };
  }, []);
  
  return (
    <div className="dashboard">
      {Object.values(metrics).map(metric => (
        <MetricCard key={metric.id} metric={metric} />
      ))}
    </div>
  );
}

const MetricCard = memo(({ metric }) => {
  // 只在值真正变化时渲染
  return (
    <div className="metric-card">
      <h3>{metric.name}</h3>
      <p>{metric.value}</p>
    </div>
  );
}, (prev, next) => {
  // 自定义比较函数
  return prev.metric.value === next.metric.value;
});

// 性能提升:
// - 更新频率:从100次/秒降到20次/秒
// - CPU使用率:从80%降到15%
// - 渲染次数:减少85%

10. 性能监控与分析

10.1 自定义性能监控

jsx
// 性能指标收集
function usePerformanceMonitor(componentName) {
  const renderCount = useRef(0);
  const renderTimes = useRef([]);
  
  useEffect(() => {
    renderCount.current += 1;
  });
  
  useEffect(() => {
    const startTime = performance.now();
    
    return () => {
      const endTime = performance.now();
      const renderTime = endTime - startTime;
      renderTimes.current.push(renderTime);
      
      // 每10次渲染报告一次
      if (renderCount.current % 10 === 0) {
        const avgTime = renderTimes.current.reduce((a, b) => a + b, 0) 
          / renderTimes.current.length;
        
        console.log(`[性能] ${componentName}:`, {
          renders: renderCount.current,
          avgRenderTime: avgTime.toFixed(2) + 'ms',
          lastRenderTime: renderTime.toFixed(2) + 'ms'
        });
        
        renderTimes.current = [];
      }
    };
  });
}

// 使用示例
function HeavyComponent(props) {
  usePerformanceMonitor('HeavyComponent');
  
  // 组件逻辑...
  return <div>...</div>;
}

10.2 React Profiler API

jsx
import { Profiler } from 'react';

function onRenderCallback(
  id,
  phase,
  actualDuration,
  baseDuration,
  startTime,
  commitTime,
  interactions
) {
  // 发送到分析服务
  analytics.track('component_render', {
    component: id,
    phase,
    actualDuration,
    baseDuration
  });
  
  // 性能警告
  if (actualDuration > 16) {
    console.warn(`[性能警告] ${id} 渲染耗时 ${actualDuration}ms`);
  }
}

function App() {
  return (
    <Profiler id="App" onRender={onRenderCallback}>
      <Routes />
    </Profiler>
  );
}

10.3 Web Vitals监控

jsx
import { getCLS, getFID, getFCP, getLCP, getTTFB } from 'web-vitals';

function reportWebVitals() {
  getCLS(sendToAnalytics);
  getFID(sendToAnalytics);
  getFCP(sendToAnalytics);
  getLCP(sendToAnalytics);
  getTTFB(sendToAnalytics);
}

function sendToAnalytics({ name, value, id }) {
  // 发送到监控服务
  fetch('/api/analytics', {
    method: 'POST',
    body: JSON.stringify({
      metric: name,
      value,
      id,
      timestamp: Date.now()
    })
  });
}

// 在应用入口调用
reportWebVitals();

11. 面试重点总结

11.1 性能优化面试题

typescript
const interviewQuestions = [
  {
    q: '如何诊断React应用的性能问题?',
    a: `
      1. React DevTools Profiler:查看组件渲染时间
      2. Chrome DevTools Performance:分析主线程活动
      3. Lighthouse:综合性能评分
      4. Why Did You Render:找出不必要的渲染
      5. Bundle Analyzer:分析打包体积
    `
  },
  
  {
    q: '列举5种常见的性能优化手段',
    a: `
      1. React.memo:避免不必要的组件渲染
      2. useMemo/useCallback:缓存计算结果和函数
      3. 虚拟滚动:处理大列表
      4. 代码分割:按需加载
      5. 图片懒加载:延迟加载非关键资源
    `
  },
  
  {
    q: '什么情况下使用useMemo?',
    a: `
      1. 昂贵的计算(如数组排序、过滤)
      2. 传递给子组件的对象/数组
      3. 依赖项变化频率低
      4. 注意:不要过度使用,有内存开销
    `
  },
  
  {
    q: 'Context性能问题如何解决?',
    a: `
      1. 拆分Context:按更新频率分离
      2. 使用useMemo包装value
      3. 考虑使用状态管理库(Redux/Zustand)
      4. Context Selector模式
    `
  },
  
  {
    q: '虚拟滚动的实现原理?',
    a: `
      1. 只渲染可见区域的元素
      2. 计算滚动位置,动态更新显示范围
      3. 使用占位元素保持滚动条高度
      4. 推荐库:react-window、react-virtuoso
    `
  }
];

11.2 实战优化检查清单

typescript
const optimizationChecklist = {
  '渲染优化': [
    '使用React.memo包裹纯展示组件',
    '使用useMemo缓存昂贵计算',
    '使用useCallback稳定函数引用',
    '避免在render中创建新对象/数组',
    '合理拆分组件,控制渲染范围'
  ],
  
  '列表优化': [
    '为列表项添加稳定的key',
    '大列表使用虚拟滚动',
    '分页或无限滚动加载数据',
    '列表项使用memo避免重渲染'
  ],
  
  '资源优化': [
    '使用代码分割和懒加载',
    '图片使用懒加载和WebP格式',
    '压缩和tree shaking',
    'CDN加速静态资源',
    '启用Gzip/Brotli压缩'
  ],
  
  '状态优化': [
    '避免不必要的全局状态',
    'Context按更新频率拆分',
    '大状态考虑useReducer',
    '合理使用状态管理库'
  ],
  
  '内存优化': [
    '清理定时器和监听器',
    '取消未完成的异步请求',
    '避免内存泄漏',
    '合理使用缓存'
  ]
};

12. 性能优化最佳实践

12.1 开发阶段

typescript
// 1. 使用严格模式
ReactDOM.render(
  <React.StrictMode>
    <App />
  </React.StrictMode>,
  document.getElementById('root')
);

// 2. 启用性能监控
if (process.env.NODE_ENV === 'development') {
  const whyDidYouRender = require('@welldone-software/why-did-you-render');
  whyDidYouRender(React, {
    trackAllPureComponents: true,
  });
}

// 3. 使用ESLint规则
{
  "extends": [
    "plugin:react-hooks/recommended"
  ],
  "rules": {
    "react-hooks/exhaustive-deps": "error"
  }
}

12.2 构建阶段

javascript
// vite.config.js
export default {
  build: {
    rollupOptions: {
      output: {
        manualChunks: {
          'react-vendor': ['react', 'react-dom'],
          'router': ['react-router-dom'],
          'ui': ['antd']
        }
      }
    },
    minify: 'terser',
    terserOptions: {
      compress: {
        drop_console: true,
        drop_debugger: true
      }
    }
  }
};

12.3 运行时监控

jsx
// 错误边界 + 性能监控
class ErrorBoundary extends React.Component {
  componentDidCatch(error, info) {
    // 发送错误信息
    analytics.trackError(error, info);
  }
  
  render() {
    return this.props.children;
  }
}

// 使用
<ErrorBoundary>
  <Profiler id="App" onRender={onRenderCallback}>
    <App />
  </Profiler>
</ErrorBoundary>