react-performance

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

React Performance Optimization

React 性能优化

Master react performance optimization for building high-performance, scalable React applications with industry best practices.
掌握React性能优化技巧,结合行业最佳实践构建高性能、可扩展的React应用。

React.memo and Component Memoization

React.memo 与组件记忆化

React.memo prevents unnecessary re-renders by memoizing component output:
typescript
import { memo } from 'react';

interface Props {
  name: string;
  onClick: () => void;
}

// Basic memoization
const ExpensiveComponent = memo(
  function ExpensiveComponent({ name, onClick }: Props) {
  console.log('Rendering ExpensiveComponent');
  return <button onClick={onClick}>{name}</button>;
});

// Custom comparison function
const CustomMemo = memo(
  function Component({ user }: { user: User }) {
    return <div>{user.name}</div>;
  },
  (prevProps, nextProps) => {
    // Return true if passing nextProps would return the same result as prevProps
    return prevProps.user.id === nextProps.user.id;
  }
);

// When to use custom comparison
const ProductCard = memo(
  function ProductCard({ product }: { product: Product }) {
    return (
      <div>
        <h3>{product.name}</h3>
        <p>${product.price}</p>
      </div>
    );
  },
  (prev, next) => {
    // Only re-render if these specific fields change
    return (
      prev.product.id === next.product.id &&
      prev.product.name === next.product.name &&
      prev.product.price === next.product.price
    );
  }
);
React.memo通过记忆化组件输出来避免不必要的重渲染:
typescript
import { memo } from 'react';

interface Props {
  name: string;
  onClick: () => void;
}

// 基础记忆化
const ExpensiveComponent = memo(
  function ExpensiveComponent({ name, onClick }: Props) {
  console.log('Rendering ExpensiveComponent');
  return <button onClick={onClick}>{name}</button>;
});

// 自定义比较函数
const CustomMemo = memo(
  function Component({ user }: { user: User }) {
    return <div>{user.name}</div>;
  },
  (prevProps, nextProps) => {
    // 如果传入nextProps和prevProps的返回结果相同,则返回true
    return prevProps.user.id === nextProps.user.id;
  }
);

// 何时使用自定义比较
const ProductCard = memo(
  function ProductCard({ product }: { product: Product }) {
    return (
      <div>
        <h3>{product.name}</h3>
        <p>${product.price}</p>
      </div>
    );
  },
  (prev, next) => {
    // 仅当这些特定字段变化时才重渲染
    return (
      prev.product.id === next.product.id &&
      prev.product.name === next.product.name &&
      prev.product.price === next.product.price
    );
  }
);

useMemo for Expensive Computations

useMemo 用于昂贵计算

typescript
import { useMemo, useState } from 'react';

function DataTable({ items }: { items: Item[] }) {
  const [filter, setFilter] = useState('');
  const [sortBy, setSortBy] = useState<'name' | 'price'>('name');

  // Expensive filtering and sorting
  const processedItems = useMemo(() => {
    console.log('Computing filtered and sorted items');
    return items
      .filter(item => item.name.toLowerCase().includes(filter.toLowerCase()))
      .sort((a, b) => {
        if (sortBy === 'name') {
          return a.name.localeCompare(b.name);
        }
        return a.price - b.price;
      });
  }, [items, filter, sortBy]);

  // Expensive aggregate calculation
  const statistics = useMemo(() => {
    console.log('Computing statistics');
    return {
      total: processedItems.reduce((sum, item) => sum + item.price, 0),
      average: processedItems.length
        ? processedItems.reduce((sum, item) => sum + item.price, 0) / processedItems.length
        : 0,
      count: processedItems.length
    };
  }, [processedItems]);

  return (
    <>
      <input value={filter} onChange={(e) => setFilter(e.target.value)} />
      <select value={sortBy} onChange={(e) => setSortBy(e.target.value as any)}>
        <option value="name">Name</option>
        <option value="price">Price</option>
      </select>
      <div>Total: ${statistics.total}</div>
      <div>Average: ${statistics.average.toFixed(2)}</div>
      <div>Count: {statistics.count}</div>
      {processedItems.map(item => (
        <div key={item.id}>{item.name} - ${item.price}</div>
      ))}
    </>
  );
}
typescript
import { useMemo, useState } from 'react';

function DataTable({ items }: { items: Item[] }) {
  const [filter, setFilter] = useState('');
  const [sortBy, setSortBy] = useState<'name' | 'price'>('name');

  // 昂贵的过滤和排序操作
  const processedItems = useMemo(() => {
    console.log('Computing filtered and sorted items');
    return items
      .filter(item => item.name.toLowerCase().includes(filter.toLowerCase()))
      .sort((a, b) => {
        if (sortBy === 'name') {
          return a.name.localeCompare(b.name);
        }
        return a.price - b.price;
      });
  }, [items, filter, sortBy]);

  // 昂贵的聚合计算
  const statistics = useMemo(() => {
    console.log('Computing statistics');
    return {
      total: processedItems.reduce((sum, item) => sum + item.price, 0),
      average: processedItems.length
        ? processedItems.reduce((sum, item) => sum + item.price, 0) / processedItems.length
        : 0,
      count: processedItems.length
    };
  }, [processedItems]);

  return (
    <>
      <input value={filter} onChange={(e) => setFilter(e.target.value)} />
      <select value={sortBy} onChange={(e) => setSortBy(e.target.value as any)}>
        <option value="name">名称</option>
        <option value="price">价格</option>
      </select>
      <div>总计: ${statistics.total}</div>
      <div>平均值: ${statistics.average.toFixed(2)}</div>
      <div>数量: {statistics.count}</div>
      {processedItems.map(item => (
        <div key={item.id}>{item.name} - ${item.price}</div>
      ))}
    </>
  );
}

useCallback for Stable Function References

useCallback 用于稳定函数引用

typescript
import { useCallback, useState, memo } from 'react';

// Child component that only re-renders when necessary
const ListItem = memo(function ListItem({
  item,
  onDelete
}: {
  item: Item;
  onDelete: (id: string) => void;
}) {
  console.log('Rendering ListItem', item.id);
  return (
    <div>
      {item.name}
      <button onClick={() => onDelete(item.id)}>Delete</button>
    </div>
  );
});

function OptimizedList({ items }: { items: Item[] }) {
  const [deletedIds, setDeletedIds] = useState<Set<string>>(new Set());

  // Without useCallback, this creates a new function on every render
  // causing ListItem to re-render even with memo
  const handleDelete = useCallback((id: string) => {
    setDeletedIds(prev => new Set([...prev, id]));
    // API call to delete
    api.deleteItem(id);
  }, []); // Empty deps means function never changes

  const handleDeleteWithDeps = useCallback((id: string) => {
    console.log('Already deleted:', deletedIds.size);
    setDeletedIds(prev => new Set([...prev, id]));
  }, [deletedIds]); // Re-create when deletedIds changes

  const visibleItems = items.filter(item => !deletedIds.has(item.id));

  return (
    <>
      {visibleItems.map(item => (
        <ListItem key={item.id} item={item} onDelete={handleDelete} />
      ))}
    </>
  );
}
typescript
import { useCallback, useState, memo } from 'react';

// 仅在必要时重渲染的子组件
const ListItem = memo(function ListItem({
  item,
  onDelete
}: {
  item: Item;
  onDelete: (id: string) => void;
}) {
  console.log('Rendering ListItem', item.id);
  return (
    <div>
      {item.name}
      <button onClick={() => onDelete(item.id)}>删除</button>
    </div>
  );
});

function OptimizedList({ items }: { items: Item[] }) {
  const [deletedIds, setDeletedIds] = useState<Set<string>>(new Set());

  // 不使用useCallback的话,每次渲染都会创建新函数
  // 导致即使使用了memo,ListItem也会重渲染
  const handleDelete = useCallback((id: string) => {
    setDeletedIds(prev => new Set([...prev, id]));
    // 调用API删除
    api.deleteItem(id);
  }, []); // 空依赖意味着函数永远不会变化

  const handleDeleteWithDeps = useCallback((id: string) => {
    console.log('已删除数量:', deletedIds.size);
    setDeletedIds(prev => new Set([...prev, id]));
  }, [deletedIds]); // 当deletedIds变化时重新创建函数

  const visibleItems = items.filter(item => !deletedIds.has(item.id));

  return (
    <>
      {visibleItems.map(item => (
        <ListItem key={item.id} item={item} onDelete={handleDelete} />
      ))}
    </>
  );
}

Code Splitting with React.lazy and Suspense

使用 React.lazy 和 Suspense 进行代码分割

typescript
import { lazy, Suspense } from 'react';
import { Routes, Route } from 'react-router-dom';

// Lazy load route components
const Dashboard = lazy(() => import('./pages/Dashboard'));
const Profile = lazy(() => import('./pages/Profile'));
const Settings = lazy(() => import('./pages/Settings'));
const Analytics = lazy(() => import('./pages/Analytics'));

// Fallback component
function LoadingSpinner() {
  return <div className="spinner">Loading...</div>;
}

function App() {
  return (
    <Suspense fallback={<LoadingSpinner />}>
      <Routes>
        <Route path="/dashboard" element={<Dashboard />} />
        <Route path="/profile" element={<Profile />} />
        <Route path="/settings" element={<Settings />} />
        <Route path="/analytics" element={<Analytics />} />
      </Routes>
    </Suspense>
  );
}

// Preload on hover for better UX
function Navigation() {
  const preloadDashboard = () => import('./pages/Dashboard');
  const preloadProfile = () => import('./pages/Profile');

  return (
    <nav>
      <a href="/dashboard" onMouseEnter={preloadDashboard}>Dashboard</a>
      <a href="/profile" onMouseEnter={preloadProfile}>Profile</a>
    </nav>
  );
}

// Nested Suspense boundaries
function DashboardLayout() {
  const Header = lazy(() => import('./components/Header'));
  const Sidebar = lazy(() => import('./components/Sidebar'));
  const Content = lazy(() => import('./components/Content'));

  return (
    <div className="dashboard">
      <Suspense fallback={<div>Loading header...</div>}>
        <Header />
      </Suspense>
      <Suspense fallback={<div>Loading sidebar...</div>}>
        <Sidebar />
      </Suspense>
      <Suspense fallback={<div>Loading content...</div>}>
        <Content />
      </Suspense>
    </div>
  );
}
typescript
import { lazy, Suspense } from 'react';
import { Routes, Route } from 'react-router-dom';

// 懒加载路由组件
const Dashboard = lazy(() => import('./pages/Dashboard'));
const Profile = lazy(() => import('./pages/Profile'));
const Settings = lazy(() => import('./pages/Settings'));
const Analytics = lazy(() => import('./pages/Analytics'));

// 加载回退组件
function LoadingSpinner() {
  return <div className="spinner">加载中...</div>;
}

function App() {
  return (
    <Suspense fallback={<LoadingSpinner />}>
      <Routes>
        <Route path="/dashboard" element={<Dashboard />} />
        <Route path="/profile" element={<Profile />} />
        <Route path="/settings" element={<Settings />} />
        <Route path="/analytics" element={<Analytics />} />
      </Routes>
    </Suspense>
  );
}

// 鼠标悬停时预加载以提升用户体验
function Navigation() {
  const preloadDashboard = () => import('./pages/Dashboard');
  const preloadProfile = () => import('./pages/Profile');

  return (
    <nav>
      <a href="/dashboard" onMouseEnter={preloadDashboard}>控制台</a>
      <a href="/profile" onMouseEnter={preloadProfile}>个人中心</a>
    </nav>
  );
}

// 嵌套Suspense边界
function DashboardLayout() {
  const Header = lazy(() => import('./components/Header'));
  const Sidebar = lazy(() => import('./components/Sidebar'));
  const Content = lazy(() => import('./components/Content'));

  return (
    <div className="dashboard">
      <Suspense fallback={<div>加载头部...</div>}>
        <Header />
      </Suspense>
      <Suspense fallback={<div>加载侧边栏...</div>}>
        <Sidebar />
      </Suspense>
      <Suspense fallback={<div>加载内容...</div>}>
        <Content />
      </Suspense>
    </div>
  );
}

Virtual Scrolling for Large Lists

虚拟滚动处理大型列表

typescript
import { FixedSizeList, VariableSizeList } from 'react-window';
import AutoSizer from 'react-virtualized-auto-sizer';

// Fixed size items
function VirtualList({ items }: { items: string[] }) {
  const Row = ({ index, style }: {
    index: number;
    style: React.CSSProperties
  }) => (
    <div style={style} className="list-item">
      {items[index]}
    </div>
  );

  return (
    <FixedSizeList
      height={600}
      itemCount={items.length}
      itemSize={35}
      width="100%"
    >
      {Row}
    </FixedSizeList>
  );
}

// Variable size items
function VariableList({ items }: { items: Post[] }) {
  const getItemSize = (index: number) => {
    // Calculate height based on content
    return items[index].content.length > 100 ? 120 : 80;
  };

  const Row = ({ index, style }: any) => (
    <div style={style} className="post">
      <h3>{items[index].title}</h3>
      <p>{items[index].content}</p>
    </div>
  );

  return (
    <VariableSizeList
      height={600}
      itemCount={items.length}
      itemSize={getItemSize}
      width="100%"
    >
      {Row}
    </VariableSizeList>
  );
}

// With AutoSizer for responsive layouts
function ResponsiveList({ items }: { items: Item[] }) {
  return (
    <div style={{ height: '100vh', width: '100%' }}>
      <AutoSizer>
        {({ height, width }) => (
          <FixedSizeList
            height={height}
            itemCount={items.length}
            itemSize={50}
            width={width}
          >
            {({ index, style }) => (
              <div style={style}>{items[index].name}</div>
            )}
          </FixedSizeList>
        )}
      </AutoSizer>
    </div>
  );
}
typescript
import { FixedSizeList, VariableSizeList } from 'react-window';
import AutoSizer from 'react-virtualized-auto-sizer';

// 固定尺寸项
function VirtualList({ items }: { items: string[] }) {
  const Row = ({ index, style }: {
    index: number;
    style: React.CSSProperties
  }) => (
    <div style={style} className="list-item">
      {items[index]}
    </div>
  );

  return (
    <FixedSizeList
      height={600}
      itemCount={items.length}
      itemSize={35}
      width="100%"
    >
      {Row}
    </FixedSizeList>
  );
}

// 可变尺寸项
function VariableList({ items }: { items: Post[] }) {
  const getItemSize = (index: number) => {
    // 根据内容计算高度
    return items[index].content.length > 100 ? 120 : 80;
  };

  const Row = ({ index, style }: any) => (
    <div style={style} className="post">
      <h3>{items[index].title}</h3>
      <p>{items[index].content}</p>
    </div>
  );

  return (
    <VariableSizeList
      height={600}
      itemCount={items.length}
      itemSize={getItemSize}
      width="100%"
    >
      {Row}
    </VariableSizeList>
  );
}

// 结合AutoSizer实现响应式布局
function ResponsiveList({ items }: { items: Item[] }) {
  return (
    <div style={{ height: '100vh', width: '100%' }}>
      <AutoSizer>
        {({ height, width }) => (
          <FixedSizeList
            height={height}
            itemCount={items.length}
            itemSize={50}
            width={width}
          >
            {({ index, style }) => (
              <div style={style}>{items[index].name}</div>
            )}
          </FixedSizeList>
        )}
      </AutoSizer>
    </div>
  );
}

React Profiler API for Performance Monitoring

React Profiler API 用于性能监控

typescript
import { Profiler, ProfilerOnRenderCallback } from 'react';

const onRenderCallback: ProfilerOnRenderCallback = (
  id, // the "id" prop of the Profiler tree that has just committed
  phase, // either "mount" (first render) or "update" (re-render)
  actualDuration, // time spent rendering the committed update
  baseDuration, // estimated time to render the entire subtree without memoization
  startTime, // when React began rendering this update
  commitTime, // when React committed this update
  interactions // the Set of interactions belonging to this update
) => {
  console.log(`${id} (${phase}) took ${actualDuration}ms`);

  // Send to analytics
  if (actualDuration > 100) {
    analytics.track('slow-render', {
      component: id,
      duration: actualDuration,
      phase
    });
  }
};

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

// Nested profilers for granular monitoring
function Dashboard() {
  return (
    <div>
      <Profiler id="Sidebar" onRender={onRenderCallback}>
        <Sidebar />
      </Profiler>
      <Profiler id="Content" onRender={onRenderCallback}>
        <Content />
      </Profiler>
    </div>
  );
}
typescript
import { Profiler, ProfilerOnRenderCallback } from 'react';

const onRenderCallback: ProfilerOnRenderCallback = (
  id, // 刚刚提交的Profiler树的"id"属性
  phase, // 取值为"mount"(首次渲染)或"update"(重渲染)
  actualDuration, // 渲染本次提交更新所花费的时间
  baseDuration, // 假设没有记忆化时,渲染整个子树的估计时间
  startTime, // React开始渲染本次更新的时间
  commitTime, // React提交本次更新的时间
  interactions // 属于本次更新的交互集合
) => {
  console.log(`${id}${phase})耗时 ${actualDuration}ms`);

  // 发送到分析工具
  if (actualDuration > 100) {
    analytics.track('slow-render', {
      component: id,
      duration: actualDuration,
      phase
    });
  }
};

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

// 嵌套Profiler实现细粒度监控
function Dashboard() {
  return (
    <div>
      <Profiler id="Sidebar" onRender={onRenderCallback}>
        <Sidebar />
      </Profiler>
      <Profiler id="Content" onRender={onRenderCallback}>
        <Content />
      </Profiler>
    </div>
  );
}

Bundle Size Optimization

包体积优化

typescript
// Use dynamic imports for large libraries
function ChartComponent() {
  const [Chart, setChart] = useState<any>(null);

  useEffect(() => {
    // Only load chart library when needed
    import('chart.js').then(module => {
      setChart(() => module.Chart);
    });
  }, []);

  if (!Chart) return <div>Loading chart...</div>;

  return <Chart data={data} />;
}

// Tree-shakeable imports
// GOOD: Import only what you need
import { format } from 'date-fns';

// BAD: Imports entire library
import moment from 'moment';

// Use webpack magic comments for chunk names
const AdminPanel = lazy(() =>
  import(/* webpackChunkName: "admin" */ './AdminPanel')
);

const UserDashboard = lazy(() =>
  import(/* webpackChunkName: "dashboard" */ './UserDashboard')
);
typescript
// 对大型库使用动态导入
function ChartComponent() {
  const [Chart, setChart] = useState<any>(null);

  useEffect(() => {
    // 仅在需要时加载图表库
    import('chart.js').then(module => {
      setChart(() => module.Chart);
    });
  }, []);

  if (!Chart) return <div>加载图表中...</div>;

  return <Chart data={data} />;
}

// 支持摇树优化的导入方式
// 推荐:仅导入需要的部分
import { format } from 'date-fns';

// 不推荐:导入整个库
import moment from 'moment';

// 使用webpack魔法注释指定chunk名称
const AdminPanel = lazy(() =>
  import(/* webpackChunkName: "admin" */ './AdminPanel')
);

const UserDashboard = lazy(() =>
  import(/* webpackChunkName: "dashboard" */ './UserDashboard')
);

Image Optimization Techniques

图片优化技巧

typescript
import { useState, useEffect } from 'react';

// Lazy loading images
function LazyImage({ src, alt, placeholder }: {
  src: string;
  alt: string;
  placeholder?: string;
}) {
  const [imageSrc, setImageSrc] = useState(placeholder || '');
  const [imageRef, setImageRef] = useState<HTMLImageElement | null>(null);

  useEffect(() => {
    if (!imageRef) return;

    const observer = new IntersectionObserver(entries => {
      entries.forEach(entry => {
        if (entry.isIntersecting) {
          setImageSrc(src);
          observer.unobserve(imageRef);
        }
      });
    });

    observer.observe(imageRef);

    return () => {
      if (imageRef) observer.unobserve(imageRef);
    };
  }, [imageRef, src]);

  return (
    <img
      ref={setImageRef}
      src={imageSrc}
      alt={alt}
      loading="lazy"
    />
  );
}

// Progressive image loading
function ProgressiveImage({ src, placeholder }: {
  src: string;
  placeholder: string;
}) {
  const [currentSrc, setCurrentSrc] = useState(placeholder);
  const [loading, setLoading] = useState(true);

  useEffect(() => {
    const img = new Image();
    img.src = src;
    img.onload = () => {
      setCurrentSrc(src);
      setLoading(false);
    };
  }, [src]);

  return (
    <img
      src={currentSrc}
      style={{
        filter: loading ? 'blur(10px)' : 'none',
        transition: 'filter 0.3s'
      }}
    />
  );
}
typescript
import { useState, useEffect } from 'react';

// 图片懒加载
function LazyImage({ src, alt, placeholder }: {
  src: string;
  alt: string;
  placeholder?: string;
}) {
  const [imageSrc, setImageSrc] = useState(placeholder || '');
  const [imageRef, setImageRef] = useState<HTMLImageElement | null>(null);

  useEffect(() => {
    if (!imageRef) return;

    const observer = new IntersectionObserver(entries => {
      entries.forEach(entry => {
        if (entry.isIntersecting) {
          setImageSrc(src);
          observer.unobserve(imageRef);
        }
      });
    });

    observer.observe(imageRef);

    return () => {
      if (imageRef) observer.unobserve(imageRef);
    };
  }, [imageRef, src]);

  return (
    <img
      ref={setImageRef}
      src={imageSrc}
      alt={alt}
      loading="lazy"
    />
  );
}

// 渐进式图片加载
function ProgressiveImage({ src, placeholder }: {
  src: string;
  placeholder: string;
}) {
  const [currentSrc, setCurrentSrc] = useState(placeholder);
  const [loading, setLoading] = useState(true);

  useEffect(() => {
    const img = new Image();
    img.src = src;
    img.onload = () => {
      setCurrentSrc(src);
      setLoading(false);
    };
  }, [src]);

  return (
    <img
      src={currentSrc}
      style={{
        filter: loading ? 'blur(10px)' : 'none',
        transition: 'filter 0.3s'
      }}
    />
  );
}

Concurrent Features: useTransition and useDeferredValue

并发特性:useTransition 和 useDeferredValue

typescript
import { useState, useTransition, useDeferredValue } from 'react';

// useTransition for non-urgent updates
function SearchResults() {
  const [query, setQuery] = useState('');
  const [results, setResults] = useState<Result[]>([]);
  const [isPending, startTransition] = useTransition();

  const handleSearch = (value: string) => {
    setQuery(value); // Urgent: update input immediately

    // Non-urgent: defer expensive search
    startTransition(() => {
      const searchResults = performExpensiveSearch(value);
      setResults(searchResults);
    });
  };

  return (
    <>
      <input
        value={query}
        onChange={e => handleSearch(e.target.value)}
        placeholder="Search..."
      />
      {isPending && <div>Searching...</div>}
      <ResultsList results={results} />
    </>
  );
}

// useDeferredValue for deferring expensive renders
function ProductList({ products }: { products: Product[] }) {
  const [query, setQuery] = useState('');
  const deferredQuery = useDeferredValue(query);

  // This filters with the deferred value
  // UI stays responsive while filtering
  const filteredProducts = useMemo(() => {
    return products.filter(p =>
      p.name.toLowerCase().includes(deferredQuery.toLowerCase())
    );
  }, [products, deferredQuery]);

  return (
    <>
      <input
        value={query}
        onChange={e => setQuery(e.target.value)}
        placeholder="Filter products..."
      />
      {query !== deferredQuery && <div>Updating...</div>}
      <div>
        {filteredProducts.map(product => (
          <ProductCard key={product.id} product={product} />
        ))}
      </div>
    </>
  );
}
typescript
import { useState, useTransition, useDeferredValue } from 'react';

// useTransition 用于非紧急更新
function SearchResults() {
  const [query, setQuery] = useState('');
  const [results, setResults] = useState<Result[]>([]);
  const [isPending, startTransition] = useTransition();

  const handleSearch = (value: string) => {
    setQuery(value); // 紧急操作:立即更新输入框

    // 非紧急操作:延迟执行昂贵的搜索
    startTransition(() => {
      const searchResults = performExpensiveSearch(value);
      setResults(searchResults);
    });
  };

  return (
    <>
      <input
        value={query}
        onChange={e => handleSearch(e.target.value)}
        placeholder="搜索..."
      />
      {isPending && <div>搜索中...</div>}
      <ResultsList results={results} />
    </>
  );
}

// useDeferredValue 用于延迟昂贵的渲染
function ProductList({ products }: { products: Product[] }) {
  const [query, setQuery] = useState('');
  const deferredQuery = useDeferredValue(query);

  // 使用延迟后的值进行过滤
  // 过滤过程中UI保持响应
  const filteredProducts = useMemo(() => {
    return products.filter(p =>
      p.name.toLowerCase().includes(deferredQuery.toLowerCase())
    );
  }, [products, deferredQuery]);

  return (
    <>
      <input
        value={query}
        onChange={e => setQuery(e.target.value)}
        placeholder="筛选产品..."
      />
      {query !== deferredQuery && <div>更新中...</div>}
      <div>
        {filteredProducts.map(product => (
          <ProductCard key={product.id} product={product} />
        ))}
      </div>
    </>
  );
}

Debouncing and Throttling

防抖与节流

typescript
import { useState, useEffect, useCallback, useRef } from 'react';

// Custom debounce hook
function useDebounce<T>(value: T, delay: number): T {
  const [debouncedValue, setDebouncedValue] = useState<T>(value);

  useEffect(() => {
    const handler = setTimeout(() => {
      setDebouncedValue(value);
    }, delay);

    return () => clearTimeout(handler);
  }, [value, delay]);

  return debouncedValue;
}

// Usage with search
function SearchWithDebounce() {
  const [query, setQuery] = useState('');
  const debouncedQuery = useDebounce(query, 500);

  useEffect(() => {
    if (debouncedQuery) {
      // Only search after user stops typing for 500ms
      performSearch(debouncedQuery);
    }
  }, [debouncedQuery]);

  return (
    <input
      value={query}
      onChange={e => setQuery(e.target.value)}
      placeholder="Search..."
    />
  );
}

// Custom throttle hook
function useThrottle<T>(value: T, limit: number): T {
  const [throttledValue, setThrottledValue] = useState<T>(value);
  const lastRan = useRef(Date.now());

  useEffect(() => {
    const handler = setTimeout(() => {
      if (Date.now() - lastRan.current >= limit) {
        setThrottledValue(value);
        lastRan.current = Date.now();
      }
    }, limit - (Date.now() - lastRan.current));

    return () => clearTimeout(handler);
  }, [value, limit]);

  return throttledValue;
}

// Throttle scroll events
function InfiniteScroll() {
  const [scrollPosition, setScrollPosition] = useState(0);
  const throttledScroll = useThrottle(scrollPosition, 200);

  useEffect(() => {
    const handleScroll = () => setScrollPosition(window.scrollY);
    window.addEventListener('scroll', handleScroll);
    return () => window.removeEventListener('scroll', handleScroll);
  }, []);

  useEffect(() => {
    // Only process scroll every 200ms
    console.log('Throttled scroll position:', throttledScroll);
  }, [throttledScroll]);

  return <div>Scroll position: {throttledScroll}</div>;
}
typescript
import { useState, useEffect, useCallback, useRef } from 'react';

// 自定义防抖Hook
function useDebounce<T>(value: T, delay: number): T {
  const [debouncedValue, setDebouncedValue] = useState<T>(value);

  useEffect(() => {
    const handler = setTimeout(() => {
      setDebouncedValue(value);
    }, delay);

    return () => clearTimeout(handler);
  }, [value, delay]);

  return debouncedValue;
}

// 结合搜索使用
function SearchWithDebounce() {
  const [query, setQuery] = useState('');
  const debouncedQuery = useDebounce(query, 500);

  useEffect(() => {
    if (debouncedQuery) {
      // 用户停止输入500ms后才执行搜索
      performSearch(debouncedQuery);
    }
  }, [debouncedQuery]);

  return (
    <input
      value={query}
      onChange={e => setQuery(e.target.value)}
      placeholder="搜索..."
    />
  );
}

// 自定义节流Hook
function useThrottle<T>(value: T, limit: number): T {
  const [throttledValue, setThrottledValue] = useState<T>(value);
  const lastRan = useRef(Date.now());

  useEffect(() => {
    const handler = setTimeout(() => {
      if (Date.now() - lastRan.current >= limit) {
        setThrottledValue(value);
        lastRan.current = Date.now();
      }
    }, limit - (Date.now() - lastRan.current));

    return () => clearTimeout(handler);
  }, [value, limit]);

  return throttledValue;
}

// 节流处理滚动事件
function InfiniteScroll() {
  const [scrollPosition, setScrollPosition] = useState(0);
  const throttledScroll = useThrottle(scrollPosition, 200);

  useEffect(() => {
    const handleScroll = () => setScrollPosition(window.scrollY);
    window.addEventListener('scroll', handleScroll);
    return () => window.removeEventListener('scroll', handleScroll);
  }, []);

  useEffect(() => {
    // 每200ms处理一次滚动事件
    console.log('节流后的滚动位置:', throttledScroll);
  }, [throttledScroll]);

  return <div>滚动位置: {throttledScroll}</div>;
}

Optimizing Context Performance

优化Context性能

typescript
import { createContext, useContext, useState, useMemo, ReactNode } from 'react';

// Split context to prevent unnecessary re-renders
const StateContext = createContext<State | null>(null);
const DispatchContext = createContext<Dispatch | null>(null);

function Provider({ children }: { children: ReactNode }) {
  const [state, setState] = useState<State>(initialState);

  // Memoize dispatch to keep it stable
  const dispatch = useMemo(
    () => ({
      updateUser: (user: User) => setState(s => ({ ...s, user })),
      updateSettings: (settings: Settings) => setState(s => ({ ...s, settings }))
    }),
    []
  );

  return (
    <StateContext.Provider value={state}>
      <DispatchContext.Provider value={dispatch}>
        {children}
      </DispatchContext.Provider>
    </StateContext.Provider>
  );
}

// Components only re-render when they use state that actually changes
function UserProfile() {
  const state = useContext(StateContext); // Re-renders on any state change
  return <div>{state?.user.name}</div>;
}

function SettingsButton() {
  const dispatch = useContext(DispatchContext); // Never re-renders
  return <button onClick={() => dispatch?.updateSettings({})}>Settings</button>;
}
typescript
import { createContext, useContext, useState, useMemo, ReactNode } from 'react';

// 拆分Context以避免不必要的重渲染
const StateContext = createContext<State | null>(null);
const DispatchContext = createContext<Dispatch | null>(null);

function Provider({ children }: { children: ReactNode }) {
  const [state, setState] = useState<State>(initialState);

  // 记忆化dispatch以保持引用稳定
  const dispatch = useMemo(
    () => ({
      updateUser: (user: User) => setState(s => ({ ...s, user })),
      updateSettings: (settings: Settings) => setState(s => ({ ...s, settings }))
    }),
    []
  );

  return (
    <StateContext.Provider value={state}>
      <DispatchContext.Provider value={dispatch}>
        {children}
      </DispatchContext.Provider>
    </StateContext.Provider>
  );
}

// 组件仅在使用的状态实际变化时重渲染
function UserProfile() {
  const state = useContext(StateContext); // 任何状态变化都会触发重渲染
  return <div>{state?.user.name}</div>;
}

function SettingsButton() {
  const dispatch = useContext(DispatchContext); // 永远不会重渲染
  return <button onClick={() => dispatch?.updateSettings({})}>设置</button>;
}

Web Workers for Heavy Computations

Web Workers 处理繁重计算

typescript
import { useEffect, useState } from 'react';

// worker.ts
// self.addEventListener('message', (e) => {
//   const result = performHeavyCalculation(e.data);
//   self.postMessage(result);
// });

function useWebWorker<T, R>(workerFn: (data: T) => R) {
  const [result, setResult] = useState<R | null>(null);
  const [error, setError] = useState<Error | null>(null);
  const [loading, setLoading] = useState(false);

  const execute = (data: T) => {
    setLoading(true);
    setError(null);

    const worker = new Worker(
      URL.createObjectURL(
        new Blob([`(${workerFn.toString()})()`], { type: 'application/javascript' })
      )
    );

    worker.postMessage(data);

    worker.onmessage = (e) => {
      setResult(e.data);
      setLoading(false);
      worker.terminate();
    };

    worker.onerror = (e) => {
      setError(new Error(e.message));
      setLoading(false);
      worker.terminate();
    };
  };

  return { result, error, loading, execute };
}

// Usage
function DataProcessor() {
  const { result, loading, execute } = useWebWorker(
    (data: number[]) => {
      // Heavy calculation runs in worker
      return data.reduce((sum, n) => sum + n * n, 0);
    }
  );

  const handleProcess = () => {
    execute(Array.from({ length: 1000000 }, (_, i) => i));
  };

  return (
    <div>
      <button onClick={handleProcess} disabled={loading}>
        Process Data
      </button>
      {loading && <div>Processing...</div>}
      {result && <div>Result: {result}</div>}
    </div>
  );
}
typescript
import { useEffect, useState } from 'react';

// worker.ts
// self.addEventListener('message', (e) => {
//   const result = performHeavyCalculation(e.data);
//   self.postMessage(result);
// });

function useWebWorker<T, R>(workerFn: (data: T) => R) {
  const [result, setResult] = useState<R | null>(null);
  const [error, setError] = useState<Error | null>(null);
  const [loading, setLoading] = useState(false);

  const execute = (data: T) => {
    setLoading(true);
    setError(null);

    const worker = new Worker(
      URL.createObjectURL(
        new Blob([`(${workerFn.toString()})()`], { type: 'application/javascript' })
      )
    );

    worker.postMessage(data);

    worker.onmessage = (e) => {
      setResult(e.data);
      setLoading(false);
      worker.terminate();
    };

    worker.onerror = (e) => {
      setError(new Error(e.message));
      setLoading(false);
      worker.terminate();
    };
  };

  return { result, error, loading, execute };
}

// 使用示例
function DataProcessor() {
  const { result, loading, execute } = useWebWorker(
    (data: number[]) => {
      // 繁重计算在worker中运行
      return data.reduce((sum, n) => sum + n * n, 0);
    }
  );

  const handleProcess = () => {
    execute(Array.from({ length: 1000000 }, (_, i) => i));
  };

  return (
    <div>
      <button onClick={handleProcess} disabled={loading}>
        处理数据
      </button>
      {loading && <div>处理中...</div>}
      {result && <div>结果: {result}</div>}
    </div>
  );
}

When to Use This Skill

何时使用本技能

Use react-performance when you need to:
  • Optimize slow-rendering components
  • Reduce bundle size with code splitting
  • Handle large lists with virtualization
  • Prevent unnecessary re-renders
  • Improve application load time
  • Optimize expensive computations
  • Build performant React applications
  • Debug performance issues
  • Implement lazy loading strategies
  • Improve Core Web Vitals scores
  • Optimize for mobile devices
  • Handle real-time data efficiently
当你需要以下操作时,使用React性能优化技能:
  • 优化渲染缓慢的组件
  • 通过代码分割减小包体积
  • 使用虚拟化处理大型列表
  • 避免不必要的重渲染
  • 提升应用加载速度
  • 优化昂贵的计算操作
  • 构建高性能React应用
  • 调试性能问题
  • 实现懒加载策略
  • 提升Core Web Vitals评分
  • 针对移动设备进行优化
  • 高效处理实时数据

Best Practices

最佳实践

  1. Profile before optimizing - Use React DevTools Profiler to identify actual bottlenecks before applying optimizations.
  2. Use React.memo wisely - Only memoize components that render often with the same props or have expensive render logic.
  3. Memoize callbacks and values - Use useCallback for functions passed to memoized children, useMemo for expensive computations.
  4. Code split by route - Lazy load route components to reduce initial bundle size and improve load time.
  5. Virtualize long lists - Use react-window or react-virtualized for lists with more than 100 items.
  6. Optimize images - Lazy load images, use appropriate formats (WebP), implement progressive loading.
  7. Debounce expensive operations - Debounce search inputs, API calls, and other expensive operations.
  8. Split context strategically - Separate read and write contexts to prevent unnecessary consumer re-renders.
  9. Monitor bundle size - Use webpack-bundle-analyzer to identify and remove large dependencies.
  10. Use concurrent features - Leverage useTransition and useDeferredValue for better perceived performance.
  1. 先分析再优化 - 在应用优化前,使用React DevTools Profiler识别实际性能瓶颈。
  2. 合理使用React.memo - 仅对频繁渲染且props不变,或渲染逻辑昂贵的组件进行记忆化。
  3. 记忆化回调和值 - 对传递给记忆化子组件的函数使用useCallback,对昂贵计算使用useMemo。
  4. 按路由进行代码分割 - 懒加载路由组件以减小初始包体积,提升加载速度。
  5. 虚拟化长列表 - 当列表项超过100个时,使用react-window或react-virtualized。
  6. 优化图片 - 懒加载图片,使用合适的格式(如WebP),实现渐进式加载。
  7. 对昂贵操作防抖 - 对搜索输入、API调用等昂贵操作进行防抖处理。
  8. 合理拆分Context - 分离读和写Context,避免不必要的消费者重渲染。
  9. 监控包体积 - 使用webpack-bundle-analyzer识别并移除大型依赖。
  10. 使用并发特性 - 利用useTransition和useDeferredValue提升感知性能。

Common Pitfalls

常见陷阱

  1. Premature optimization - Don't optimize without measuring. Profile first, then optimize bottlenecks.
  2. Overusing memo - Memoizing everything adds overhead. Only memoize when there's a measurable benefit.
  3. Wrong dependencies - Missing dependencies in useMemo/useCallback leads to stale closures and bugs.
  4. Not measuring impact - Always measure performance improvements with React Profiler or browser tools.
  5. Ignoring bundle size - Importing large libraries for small features significantly impacts load time.
  6. Memoizing primitives - useMemo is unnecessary for primitive values or simple calculations.
  7. Not using key prop - Missing or incorrect keys in lists cause unnecessary re-renders and bugs.
  8. Inline function definitions - Creating functions inline in JSX prevents React.memo from working effectively.
  9. Not code splitting - Loading entire app upfront increases initial load time dramatically.
  10. Forgetting about network - Optimize data fetching, use pagination, implement proper caching strategies.
  1. 过早优化 - 不要在未测量的情况下进行优化。先分析,再针对瓶颈进行优化。
  2. 过度使用记忆化 - 对所有内容进行记忆化会增加开销。仅在有可衡量的收益时使用。
  3. 依赖错误 - useMemo/useCallback中遗漏依赖会导致闭包过时和bug。
  4. 不衡量优化效果 - 始终使用React Profiler或浏览器工具衡量性能提升。
  5. 忽略包体积 - 为了小功能导入大型库会显著影响加载时间。
  6. 记忆化原始值 - 对原始值或简单计算使用useMemo是不必要的。
  7. 不使用key属性 - 列表中缺失或不正确的key会导致不必要的重渲染和bug。
  8. 内联函数定义 - 在JSX中内联定义函数会导致React.memo失效。
  9. 不进行代码分割 - 一次性加载整个应用会大幅增加初始加载时间。
  10. 忽略网络优化 - 优化数据获取,使用分页,实现适当的缓存策略。

Resources

参考资源