正在加载,请稍候…

React 性能优化:记忆化、虚拟列表与代码分割

掌握 React 性能优化技巧:useMemo、useCallback、React.memo、虚拟列表、懒加载、Suspense、useTransition 以

React 性能优化:记忆化、虚拟列表与代码分割

React 性能优化

React 默认速度很快,但复杂应用会遭遇不必要的重渲染和过大的打包体积。本指南涵盖系统性的性能优化方法。

useMemo 和 useCallback

function ProductList({ products, onSelect }: Props) {
  const [filter, setFilter] = useState('');

  // 仅当依赖项变化时重新计算
  const filtered = useMemo(() => {
    return products
      .filter(p => p.name.toLowerCase().includes(filter.toLowerCase()))
      .sort((a, b) => a.name > b.name ? 1 : -1);
  }, [products, filter]);

  // 为记忆化子组件提供稳定引用
  const handleSelect = useCallback((id: string) => {
    onSelect(id);
  }, [onSelect]);

  return (
    <div>
      <input value={filter} onChange={e => setFilter(e.target.value)} />
      {filtered.map(product => (
        <ProductCard 
          key={product.id}
          product={product}
          onSelect={handleSelect}
        />
      ))}
    </div>
  );
}

const ProductCard = React.memo(({ product, onSelect }: Props) => {
  return <div onClick={() => onSelect(product.id)}>{product.name}</div>;
});

React 性能优化:记忆化、虚拟列表与代码分割 插图

大数据集的虚拟列表

import { FixedSizeList } from 'react-window';

const Row = ({ index, style, data }: any) => {
  const item = data.items[index];
  return (
    <div style={style} onClick={() => data.onSelect(item.id)}>
      {item.name} - ${item.price}
    </div>
  );
};

function VirtualProductList({ products, onSelect }: Props) {
  const itemData = useMemo(
    () => ({ items: products, onSelect }),
    [products, onSelect]
  );
  
  return (
    <FixedSizeList
      height={600}
      itemCount={products.length}
      itemSize={60}
      itemData={itemData}
    >
      {Row}
    </FixedSizeList>
  );
}

代码分割与懒加载

const Dashboard = lazy(() => import('./pages/Dashboard'));
const Analytics = lazy(() => import('./pages/Analytics'));

function App() {
  return (
    <Suspense fallback={<PageSkeleton />}>
      <Routes>
        <Route path="/" element={<Dashboard />} />
        <Route path="/analytics" element={<Analytics />} />
      </Routes>
    </Suspense>
  );
}

React 性能优化:记忆化、虚拟列表与代码分割 插图

useTransition 和 useDeferredValue

function SearchPage() {
  const [query, setQuery] = useState('');
  const [isPending, startTransition] = useTransition();

  const handleChange = (e: React.ChangeEvent<HTMLInputElement>) => {
    const value = e.target.value;
    setQuery(value); // 紧急:立即更新输入
    
    startTransition(() => {
      // 非紧急:如果用户再次输入可中断
      setSearchResults(performSearch(value));
    });
  };

  return (
    <div>
      <input value={query} onChange={handleChange} />
      {isPending ? <Spinner /> : <Results />}
    </div>
  );
}

// useDeferredValue 用于昂贵渲染
function SearchResults({ query }: { query: string }) {
  const deferredQuery = useDeferredValue(query);
  const isStale = query !== deferredQuery;
  
  const results = useMemo(
    () => performExpensiveSearch(deferredQuery),
    [deferredQuery]
  );
  
  return (
    <div style={{ opacity: isStale ? 0.7 : 1 }}>
      {results.map(r => <ResultItem key={r.id} result={r} />)}
    </div>
  );
}

Context 优化

// 不好:单个 context 导致所有消费者重渲染
const AppContext = createContext({ user: null, cart: [], theme: 'light' });

// 好:按更新频率拆分 context
const UserContext = createContext<User | null>(null);
const CartContext = createContext<Cart>({ items: [], total: 0 });
const ThemeContext = createContext<'light' | 'dark'>('light');

React 性能优化:记忆化、虚拟列表与代码分割 插图

性能检查清单

  • 首先使用 React DevTools Profiler 进行分析
  • 对 props 稳定的组件添加 React.memo
  • 对昂贵计算使用 useMemo
  • 对传递给记忆化子组件的处理函数使用 useCallback
  • 对超过 100 项的列表使用虚拟化
  • 对路由和重型组件进行代码分割
  • 对非紧急更新使用 useTransition
  • 避免在 JSX props 中使用内联对象/数组

总结

先分析,再优化。过度记忆化会增加复杂性而无益。React 的内置工具(useTransition、useDeferredValue)能优雅地处理并发渲染。