学习目标

  • 理解React性能优化原理
  • 掌握组件优化技巧
  • 学会渲染优化方法
  • 理解内存管理和清理
  • 构建高性能React应用

学习时间安排

总时长:8-9小时

  • 性能优化基础:2小时
  • 组件优化技巧:2.5小时
  • 渲染优化:2小时
  • 内存管理:1小时
  • 实践项目:2-3小时

第一部分:性能优化基础 (2小时)

1.1 React性能优化原理

性能问题识别(详细注释版)
// src/utils/performanceMonitor.js
// 性能监控工具
import { useEffect, useRef } from 'react';

// 定义性能监控Hook
export function usePerformanceMonitor(componentName) {
  // 使用useRef Hook保存渲染开始时间
  const renderStartTime = useRef(null);
  
  // 使用useRef Hook保存渲染次数
  const renderCount = useRef(0);

  // 组件挂载时记录开始时间
  useEffect(() => {
    renderStartTime.current = performance.now();
    renderCount.current = 0;
    
    // 返回清理函数
    return () => {
      const renderTime = performance.now() - renderStartTime.current;
      const averageRenderTime = renderCount.current > 0 
        ? renderTime / renderCount.current 
        : 0;
      
      console.log(`Component: ${componentName}`);
      console.log(`Total render time: ${renderTime.toFixed(2)}ms`);
      console.log(`Total renders: ${renderCount.current}`);
      console.log(`Average render time: ${averageRenderTime.toFixed(2)}ms`);
    };
  }, [componentName]);

  // 每次渲染时记录
  useEffect(() => {
    renderCount.current += 1;
    const renderTime = performance.now() - (renderStartTime.current || 0);
    
    if (renderTime > 16) {
      console.warn(`Slow render detected in ${componentName}: ${renderTime.toFixed(2)}ms`);
    }
  });
}

// 定义性能测量函数
export function measurePerformance(fn, label) {
  const start = performance.now();
  const result = fn();
  const end = performance.now();
  const duration = end - start;
  
  console.log(`${label}: ${duration.toFixed(2)}ms`);
  
  if (duration > 16) {
    console.warn(`Slow operation detected: ${label} took ${duration.toFixed(2)}ms`);
  }
  
  return result;
}

// 定义性能分析器
export class PerformanceProfiler {
  constructor() {
    this.metrics = [];
    this.startTime = null;
  }

  // 开始测量
  start(label) {
    this.startTime = performance.now();
    this.currentLabel = label;
  }

  // 结束测量
  end() {
    if (this.startTime && this.currentLabel) {
      const duration = performance.now() - this.startTime;
      this.metrics.push({
        label: this.currentLabel,
        duration: duration,
        timestamp: new Date().toISOString()
      });
      
      if (duration > 16) {
        console.warn(`Slow operation: ${this.currentLabel} took ${duration.toFixed(2)}ms`);
      }
      
      this.startTime = null;
      this.currentLabel = null;
    }
  }

  // 获取报告
  getReport() {
    const totalTime = this.metrics.reduce((sum, m) => sum + m.duration, 0);
    const averageTime = this.metrics.length > 0 
      ? totalTime / this.metrics.length 
      : 0;
    
    return {
      totalOperations: this.metrics.length,
      totalTime: totalTime.toFixed(2),
      averageTime: averageTime.toFixed(2),
      metrics: this.metrics
    };
  }

  // 清除记录
  clear() {
    this.metrics = [];
  }
}

1.2 React DevTools Profiler

Profiler组件使用(详细注释版)
// src/components/ProfiledComponent.js
// 导入React和Profiler
import React, { Profiler } from 'react';

// 定义性能回调函数
// 这个函数会在组件渲染时被调用
function onRenderCallback(id, phase, actualDuration, baseDuration, startTime, commitTime) {
  // id: 被测量的组件的id
  // phase: 渲染阶段,'mount' 或 'update'
  // actualDuration: 本次渲染实际花费的时间(毫秒)
  // baseDuration: 估算的不使用优化的情况下渲染需要的时间
  // startTime: 本次渲染开始的时间戳
  // commitTime: React提交本次更新的时间戳
  
  console.log('Profiler:', {
    id,
    phase,
    actualDuration: `${actualDuration.toFixed(2)}ms`,
    baseDuration: `${baseDuration.toFixed(2)}ms`,
    startTime: `${startTime.toFixed(2)}ms`,
    commitTime: `${commitTime.toFixed(2)}ms`
  });
  
  // 如果渲染时间过长,发出警告
  if (actualDuration > 16) {
    console.warn(`Slow render detected: ${id} took ${actualDuration.toFixed(2)}ms`);
  }
}

// 定义被测量的组件
function ExpensiveComponent({ data }) {
  // 模拟昂贵的计算
  const processedData = data.map(item => {
    let sum = 0;
    for (let i = 0; i < 1000; i++) {
      sum += item.value * i;
    }
    return { ...item, processed: sum };
  });
  
  return (
    <div>
      <h3>Expensive Component</h3>
      <ul>
        {processedData.map(item => (
          <li key={item.id}>{item.name}: {item.processed}</li>
        ))}
      </ul>
    </div>
  );
}

// 定义使用Profiler的组件
function ProfiledComponent({ data }) {
  return (
    <Profiler id="ExpensiveComponent" onRender={onRenderCallback}>
      <ExpensiveComponent data={data} />
    </Profiler>
  );
}

// 导出组件
export default ProfiledComponent;

第二部分:组件优化技巧 (2.5小时)

2.1 React.memo优化

基础memo使用(详细注释版)
// src/components/MemoizedComponent.js
// 导入React和memo
import React, { memo } from 'react';

// 定义普通组件
function RegularComponent({ name, age, address }) {
  console.log('RegularComponent rendered');
  
  return (
    <div>
      <h3>Regular Component</h3>
      <p>Name: {name}</p>
      <p>Age: {age}</p>
      <p>Address: {address.street}, {address.city}</p>
    </div>
  );
}

// 定义使用memo优化的组件
// memo会对props进行浅比较,只有当props发生变化时才重新渲染
const MemoizedComponent = memo(function MemoizedComponent({ name, age, address }) {
  console.log('MemoizedComponent rendered');
  
  return (
    <div>
      <h3>Memoized Component</h3>
      <p>Name: {name}</p>
      <p>Age: {age}</p>
      <p>Address: {address.street}, {address.city}</p>
    </div>
  );
});

// 定义自定义比较函数
// 只有当name或age变化时才重新渲染,忽略address的变化
const CustomMemoizedComponent = memo(
  function CustomMemoizedComponent({ name, age, address }) {
    console.log('CustomMemoizedComponent rendered');
    
    return (
      <div>
        <h3>Custom Memoized Component</h3>
        <p>Name: {name}</p>
        <p>Age: {age}</p>
        <p>Address: {address.street}, {address.city}</p>
      </div>
    );
  },
  // 自定义比较函数
  // 返回true表示props相等,不重新渲染
  // 返回false表示props不相等,需要重新渲染
  (prevProps, nextProps) => {
    return prevProps.name === nextProps.name && 
           prevProps.age === nextProps.age;
  }
);

// 导出组件
export { RegularComponent, MemoizedComponent, CustomMemoizedComponent };
memo高级用法(详细注释版)
// src/components/AdvancedMemo.js
// 导入React、memo、useState、useCallback
import React, { memo, useState, useCallback } from 'react';

// 定义子组件
const ChildComponent = memo(function ChildComponent({ 
  name, 
  onClick, 
  items 
}) {
  console.log('ChildComponent rendered');
  
  return (
    <div>
      <h3>Child Component</h3>
      <p>Name: {name}</p>
      <button onClick={onClick}>Click me</button>
      <ul>
        {items.map(item => (
          <li key={item.id}>{item.text}</li>
        ))}
      </ul>
    </div>
  );
});

// 定义父组件
function AdvancedMemo() {
  // 使用useState Hook管理状态
  const [count, setCount] = useState(0);
  const [name, setName] = useState('John');
  const [items, setItems] = useState([
    { id: 1, text: 'Item 1' },
    { id: 2, text: 'Item 2' }
  ]);

  // 使用useCallback Hook记忆化函数
  // 只有当依赖项变化时才创建新函数
  const handleClick = useCallback(() => {
    console.log('Button clicked');
    setCount(prev => prev + 1);
  }, []);

  // 处理添加项目
  const handleAddItem = useCallback(() => {
    setItems(prev => [...prev, { 
      id: Date.now(), 
      text: `Item ${prev.length + 1}` 
    }]);
  }, []);

  return (
    <div>
      <h2>Advanced Memo Example</h2>
      <p>Count: {count}</p>
      
      <div>
        <input
          type="text"
          value={name}
          onChange={(e) => setName(e.target.value)}
          placeholder="Enter name"
        />
      </div>
      
      <div>
        <button onClick={handleAddItem}>Add Item</button>
      </div>
      
      {/* 使用memo优化的子组件 */}
      <ChildComponent 
        name={name}
        onClick={handleClick}
        items={items}
      />
    </div>
  );
}

// 导出组件
export default AdvancedMemo;

2.2 useMemo Hook

useMemo基础使用(详细注释版)
// src/components/UseMemoExample.js
// 导入React、useState、useMemo
import React, { useState, useMemo } from 'react';

// 定义昂贵的计算函数
function expensiveCalculation(numbers) {
  console.log('Expensive calculation running...');
  
  // 模拟昂贵的计算
  let sum = 0;
  for (let i = 0; i < numbers.length; i++) {
    for (let j = 0; j < 1000000; j++) {
      sum += numbers[i] * j;
    }
  }
  
  return sum;
}

// 定义使用useMemo的组件
function UseMemoExample() {
  // 使用useState Hook管理状态
  const [numbers, setNumbers] = useState([1, 2, 3, 4, 5]);
  const [count, setCount] = useState(0);
  const [filter, setFilter] = useState('all');

  // 使用useMemo Hook记忆化计算结果
  // 只有当numbers变化时才重新计算
  const expensiveValue = useMemo(() => {
    return expensiveCalculation(numbers);
  }, [numbers]);

  // 使用useMemo Hook过滤和排序数据
  // 只有当numbers或filter变化时才重新计算
  const filteredAndSortedNumbers = useMemo(() => {
    console.log('Filtering and sorting...');
    
    let filtered = numbers;
    
    if (filter === 'even') {
      filtered = numbers.filter(n => n % 2 === 0);
    } else if (filter === 'odd') {
      filtered = numbers.filter(n => n % 2 !== 0);
    }
    
    return [...filtered].sort((a, b) => a - b);
  }, [numbers, filter]);

  // 处理添加数字
  const handleAddNumber = () => {
    setNumbers(prev => [...prev, Math.floor(Math.random() * 100)]);
  };

  // 处理移除数字
  const handleRemoveNumber = (index) => {
    setNumbers(prev => prev.filter((_, i) => i !== index));
  };

  return (
    <div>
      <h2>useMemo Example</h2>
      
      <div>
        <p>Count: {count}</p>
        <button onClick={() => setCount(count + 1)}>Increment Count</button>
      </div>
      
      <div>
        <label>
          Filter:
          <select value={filter} onChange={(e) => setFilter(e.target.value)}>
            <option value="all">All</option>
            <option value="even">Even</option>
            <option value="odd">Odd</option>
          </select>
        </label>
      </div>
      
      <div>
        <button onClick={handleAddNumber}>Add Number</button>
        <ul>
          {numbers.map((number, index) => (
            <li key={index}>
              {number}
              <button onClick={() => handleRemoveNumber(index)}>Remove</button>
            </li>
          ))}
        </ul>
      </div>
      
      <div>
        <h3>Filtered and Sorted Numbers:</h3>
        <ul>
          {filteredAndSortedNumbers.map((number, index) => (
            <li key={index}>{number}</li>
          ))}
        </ul>
      </div>
      
      <div>
        <h3>Expensive Calculation Result:</h3>
        <p>{expensiveValue}</p>
        <p>Note: This value is memoized and only recalculates when numbers change.</p>
      </div>
    </div>
  );
}

// 导出组件
export default UseMemoExample;

2.3 useCallback Hook

useCallback基础使用(详细注释版)
// src/components/UseCallbackExample.js
// 导入React、useState、useCallback、memo
import React, { useState, useCallback, memo } from 'react';

// 定义使用memo优化的子组件
const Button = memo(function Button({ onClick, label }) {
  console.log(`Button ${label} rendered`);
  
  return (
    <button onClick={onClick} className="btn">
      {label}
    </button>
  );
});

// 定义使用memo优化的列表项组件
const ListItem = memo(function ListItem({ item, onDelete, onEdit }) {
  console.log(`ListItem ${item.id} rendered`);
  
  return (
    <div className="list-item">
      <span>{item.name}</span>
      <div>
        <button onClick={() => onEdit(item.id)}>Edit</button>
        <button onClick={() => onDelete(item.id)}>Delete</button>
      </div>
    </div>
  );
});

// 定义使用useCallback的组件
function UseCallbackExample() {
  // 使用useState Hook管理状态
  const [count, setCount] = useState(0);
  const [items, setItems] = useState([
    { id: 1, name: 'Item 1' },
    { id: 2, name: 'Item 2' },
    { id: 3, name: 'Item 3' }
  ]);

  // 使用useCallback Hook记忆化函数
  // 这个函数不会在每次渲染时重新创建
  const handleIncrement = useCallback(() => {
    setCount(prev => prev + 1);
  }, []);

  // 使用useCallback Hook记忆化函数
  // 这个函数不会在每次渲染时重新创建
  const handleDecrement = useCallback(() => {
    setCount(prev => prev - 1);
  }, []);

  // 使用useCallback Hook记忆化函数
  // 只有当items变化时才创建新函数
  const handleDelete = useCallback((id) => {
    setItems(prev => prev.filter(item => item.id !== id));
  }, []);

  // 使用useCallback Hook记忆化函数
  // 这个函数不会在每次渲染时重新创建
  const handleEdit = useCallback((id) => {
    const newName = prompt('Enter new name:');
    if (newName) {
      setItems(prev => prev.map(item =>
        item.id === id ? { ...item, name: newName } : item
      ));
    }
  }, []);

  // 处理添加项目
  const handleAddItem = useCallback(() => {
    setItems(prev => [...prev, {
      id: Date.now(),
      name: `Item ${prev.length + 1}`
    }]);
  }, []);

  return (
    <div>
      <h2>useCallback Example</h2>
      
      <div>
        <p>Count: {count}</p>
        <Button onClick={handleIncrement} label="Increment" />
        <Button onClick={handleDecrement} label="Decrement" />
      </div>
      
      <div>
        <button onClick={handleAddItem}>Add Item</button>
        <ul>
          {items.map(item => (
            <ListItem
              key={item.id}
              item={item}
              onDelete={handleDelete}
              onEdit={handleEdit}
            />
          ))}
        </ul>
      </div>
    </div>
  );
}

// 导出组件
export default UseCallbackExample;

2.4 组件拆分优化

组件拆分示例(详细注释版)
// src/components/OptimizedList.js
// 导入React、useState、useCallback、memo、useMemo
import React, { useState, useCallback, memo, useMemo } from 'react';

// 定义列表项组件
// 使用memo优化,只有当props变化时才重新渲染
const ListItem = memo(function ListItem({ 
  item, 
  isSelected, 
  onSelect, 
  onToggle 
}) {
  console.log(`ListItem ${item.id} rendered`);
  
  return (
    <div 
      className={`list-item ${isSelected ? 'selected' : ''}`}
      onClick={() => onSelect(item.id)}
    >
      <input
        type="checkbox"
        checked={item.completed}
        onChange={() => onToggle(item.id)}
        onClick={(e) => e.stopPropagation()}
      />
      <span>{item.name}</span>
      <span className="item-date">
        {new Date(item.createdAt).toLocaleDateString()}
      </span>
    </div>
  );
});

// 定义列表头部组件
// 使用memo优化
const ListHeader = memo(function ListHeader({ 
  total, 
  selected, 
  onSelectAll, 
  onClearSelection 
}) {
  console.log('ListHeader rendered');
  
  return (
    <div className="list-header">
      <h3>Items ({total})</h3>
      <div>
        <button onClick={onSelectAll}>Select All</button>
        <button onClick={onClearSelection}>Clear Selection</button>
        <span>Selected: {selected}</span>
      </div>
    </div>
  );
});

// 定义列表过滤器组件
// 使用memo优化
const ListFilter = memo(function ListFilter({ 
  filter, 
  onFilterChange 
}) {
  console.log('ListFilter rendered');
  
  return (
    <div className="list-filter">
      <label>
        Filter:
        <select value={filter} onChange={(e) => onFilterChange(e.target.value)}>
          <option value="all">All</option>
          <option value="completed">Completed</option>
          <option value="pending">Pending</option>
        </select>
      </label>
    </div>
  );
});

// 定义优化后的列表组件
function OptimizedList() {
  // 使用useState Hook管理状态
  const [items, setItems] = useState([
    { id: 1, name: 'Item 1', completed: false, createdAt: new Date() },
    { id: 2, name: 'Item 2', completed: true, createdAt: new Date() },
    { id: 3, name: 'Item 3', completed: false, createdAt: new Date() }
  ]);
  const [selectedItems, setSelectedItems] = useState([]);
  const [filter, setFilter] = useState('all');

  // 使用useMemo Hook记忆化过滤后的列表
  // 只有当items或filter变化时才重新计算
  const filteredItems = useMemo(() => {
    console.log('Filtering items...');
    
    switch (filter) {
      case 'completed':
        return items.filter(item => item.completed);
      case 'pending':
        return items.filter(item => !item.completed);
      default:
        return items;
    }
  }, [items, filter]);

  // 使用useCallback Hook记忆化函数
  const handleSelect = useCallback((id) => {
    setSelectedItems(prev => 
      prev.includes(id) 
        ? prev.filter(itemId => itemId !== id)
        : [...prev, id]
    );
  }, []);

  // 使用useCallback Hook记忆化函数
  const handleToggle = useCallback((id) => {
    setItems(prev => prev.map(item =>
      item.id === id ? { ...item, completed: !item.completed } : item
    ));
  }, []);

  // 使用useCallback Hook记忆化函数
  const handleSelectAll = useCallback(() => {
    setSelectedItems(filteredItems.map(item => item.id));
  }, [filteredItems]);

  // 使用useCallback Hook记忆化函数
  const handleClearSelection = useCallback(() => {
    setSelectedItems([]);
  }, []);

  // 使用useCallback Hook记忆化函数
  const handleFilterChange = useCallback((newFilter) => {
    setFilter(newFilter);
  }, []);

  return (
    <div className="optimized-list">
      <ListHeader
        total={items.length}
        selected={selectedItems.length}
        onSelectAll={handleSelectAll}
        onClearSelection={handleClearSelection}
      />
      
      <ListFilter
        filter={filter}
        onFilterChange={handleFilterChange}
      />
      
      <div className="list-items">
        {filteredItems.map(item => (
          <ListItem
            key={item.id}
            item={item}
            isSelected={selectedItems.includes(item.id)}
            onSelect={handleSelect}
            onToggle={handleToggle}
          />
        ))}
      </div>
    </div>
  );
}

// 导出组件
export default OptimizedList;

第三部分:渲染优化 (2小时)

3.1 虚拟滚动

虚拟滚动实现(详细注释版)
// src/components/VirtualizedList.js
// 导入React、useState、useRef、useMemo、useCallback
import React, { useState, useRef, useMemo, useCallback, useEffect } from 'react';

// 定义虚拟滚动组件
function VirtualizedList({ items, itemHeight = 50, containerHeight = 400 }) {
  // 使用useState Hook管理状态
  const [scrollTop, setScrollTop] = useState(0);
  
  // 使用useRef Hook保存容器引用
  const containerRef = useRef(null);

  // 计算可见范围
  // 使用useMemo Hook记忆化计算结果
  const visibleRange = useMemo(() => {
    const startIndex = Math.floor(scrollTop / itemHeight);
    const endIndex = Math.min(
      startIndex + Math.ceil(containerHeight / itemHeight) + 1,
      items.length
    );
    
    return { startIndex, endIndex };
  }, [scrollTop, itemHeight, containerHeight, items.length]);

  // 计算可见项目
  const visibleItems = useMemo(() => {
    return items.slice(visibleRange.startIndex, visibleRange.endIndex);
  }, [items, visibleRange.startIndex, visibleRange.endIndex]);

  // 计算总高度
  const totalHeight = items.length * itemHeight;

  // 计算偏移量
  const offsetY = visibleRange.startIndex * itemHeight;

  // 处理滚动事件
  const handleScroll = useCallback((e) => {
    setScrollTop(e.target.scrollTop);
  }, []);

  return (
    <div
      ref={containerRef}
      className="virtualized-list"
      style={{
        height: containerHeight,
        overflow: 'auto',
        position: 'relative'
      }}
      onScroll={handleScroll}
    >
      {/* 占位元素,保持总高度 */}
      <div style={{ height: totalHeight, position: 'relative' }}>
        {/* 可见项目容器 */}
        <div
          style={{
            position: 'absolute',
            top: offsetY,
            left: 0,
            right: 0
          }}
        >
          {visibleItems.map((item, index) => (
            <div
              key={item.id || visibleRange.startIndex + index}
              style={{
                height: itemHeight,
                display: 'flex',
                alignItems: 'center',
                padding: '0 10px',
                borderBottom: '1px solid #ddd'
              }}
            >
              {item.content || item.name || `Item ${visibleRange.startIndex + index + 1}`}
            </div>
          ))}
        </div>
      </div>
    </div>
  );
}

// 定义使用虚拟滚动的组件
function VirtualizedListExample() {
  // 生成大量数据
  const items = useMemo(() => {
    return Array.from({ length: 10000 }, (_, i) => ({
      id: i,
      name: `Item ${i + 1}`,
      content: `This is item ${i + 1} with some content`
    }));
  }, []);

  return (
    <div>
      <h2>Virtualized List Example</h2>
      <p>Total items: {items.length}</p>
      <VirtualizedList items={items} itemHeight={50} containerHeight={400} />
    </div>
  );
}

// 导出组件
export default VirtualizedListExample;

3.2 懒加载和代码分割

懒加载组件(详细注释版)
// src/components/LazyLoadedComponent.js
// 导入React、lazy、Suspense
import React, { lazy, Suspense } from 'react';

// 使用lazy函数动态导入组件
// 这会将组件代码分割成单独的chunk,只在需要时加载
const HeavyComponent = lazy(() => import('./HeavyComponent'));
const AnotherHeavyComponent = lazy(() => import('./AnotherHeavyComponent'));

// 定义加载中组件
function LoadingFallback() {
  return (
    <div className="loading-fallback">
      <div className="spinner"></div>
      <p>Loading component...</p>
    </div>
  );
}

// 定义使用懒加载的组件
function LazyLoadedComponent() {
  const [showHeavy, setShowHeavy] = React.useState(false);
  const [showAnother, setShowAnother] = React.useState(false);

  return (
    <div>
      <h2>Lazy Loaded Components</h2>
      
      <div>
        <button onClick={() => setShowHeavy(!showHeavy)}>
          {showHeavy ? 'Hide' : 'Show'} Heavy Component
        </button>
        
        <button onClick={() => setShowAnother(!showAnother)}>
          {showAnother ? 'Hide' : 'Show'} Another Heavy Component
        </button>
      </div>
      
      {/* 使用Suspense包装懒加载组件 */}
      {/* fallback属性指定加载时显示的组件 */}
      <Suspense fallback={<LoadingFallback />}>
        {showHeavy && <HeavyComponent />}
        {showAnother && <AnotherHeavyComponent />}
      </Suspense>
    </div>
  );
}

// 导出组件
export default LazyLoadedComponent;
路由级别的代码分割(详细注释版)
// src/App.js
// 导入React、lazy、Suspense
import React, { lazy, Suspense } from 'react';
// 导入路由组件
import { BrowserRouter, Routes, Route } from 'react-router-dom';
// 导入布局组件
import Layout from './components/Layout';

// 使用lazy函数动态导入页面组件
// 这些组件会被分割成独立的chunk
const Home = lazy(() => import('./pages/Home'));
const About = lazy(() => import('./pages/About'));
const Blog = lazy(() => import('./pages/Blog'));
const Contact = lazy(() => import('./pages/Contact'));

// 定义加载中组件
function PageLoader() {
  return (
    <div className="page-loader">
      <div className="spinner"></div>
      <p>Loading page...</p>
    </div>
  );
}

// 定义主应用组件
function App() {
  return (
    <BrowserRouter>
      <Layout>
        {/* 使用Suspense包装所有路由 */}
        <Suspense fallback={<PageLoader />}>
          <Routes>
            <Route path="/" element={<Home />} />
            <Route path="/about" element={<About />} />
            <Route path="/blog" element={<Blog />} />
            <Route path="/contact" element={<Contact />} />
          </Routes>
        </Suspense>
      </Layout>
    </BrowserRouter>
  );
}

// 导出App组件
export default App;

3.3 图片懒加载

图片懒加载组件(详细注释版)
// src/components/LazyImage.js
// 导入React、useState、useRef、useEffect
import React, { useState, useRef, useEffect } from 'react';

// 定义懒加载图片组件
function LazyImage({ 
  src, 
  alt, 
  placeholder = 'data:image/svg+xml,%3Csvg xmlns="http://www.w3.org/2000/svg"%3E%3C/svg%3E',
  className = '',
  ...props 
}) {
  // 使用useState Hook管理加载状态
  const [isLoaded, setIsLoaded] = useState(false);
  const [isInView, setIsInView] = useState(false);
  
  // 使用useRef Hook保存图片引用
  const imgRef = useRef(null);

  // 使用IntersectionObserver API检测图片是否进入视口
  useEffect(() => {
    const observer = new IntersectionObserver(
      (entries) => {
        entries.forEach(entry => {
          if (entry.isIntersecting) {
            setIsInView(true);
            observer.disconnect();
          }
        });
      },
      {
        rootMargin: '50px' // 提前50px开始加载
      }
    );

    if (imgRef.current) {
      observer.observe(imgRef.current);
    }

    return () => {
      if (imgRef.current) {
        observer.unobserve(imgRef.current);
      }
    };
  }, []);

  // 处理图片加载完成
  const handleLoad = () => {
    setIsLoaded(true);
  };

  // 处理图片加载错误
  const handleError = () => {
    console.error('Failed to load image:', src);
  };

  return (
    <div 
      ref={imgRef}
      className={`lazy-image-container ${className}`}
      style={{ position: 'relative', overflow: 'hidden' }}
    >
      {/* 占位图片 */}
      {!isLoaded && (
        <img
          src={placeholder}
          alt=""
          className="lazy-image-placeholder"
          style={{
            position: 'absolute',
            top: 0,
            left: 0,
            width: '100%',
            height: '100%',
            objectFit: 'cover',
            filter: 'blur(10px)',
            transition: 'opacity 0.3s'
          }}
        />
      )}
      
      {/* 实际图片 */}
      {isInView && (
        <img
          src={src}
          alt={alt}
          onLoad={handleLoad}
          onError={handleError}
          className={`lazy-image ${isLoaded ? 'loaded' : ''}`}
          style={{
            width: '100%',
            height: '100%',
            objectFit: 'cover',
            opacity: isLoaded ? 1 : 0,
            transition: 'opacity 0.3s'
          }}
          {...props}
        />
      )}
    </div>
  );
}

// 定义图片网格组件
function LazyImageGrid({ images }) {
  return (
    <div className="image-grid">
      {images.map((image, index) => (
        <LazyImage
          key={image.id || index}
          src={image.src}
          alt={image.alt || `Image ${index + 1}`}
          className="grid-image"
        />
      ))}
    </div>
  );
}

// 导出组件
export { LazyImage, LazyImageGrid };

第四部分:内存管理 (1小时)

4.1 内存泄漏预防

内存泄漏预防(详细注释版)
// src/hooks/useCleanup.js
// 导入React、useEffect、useRef
import { useEffect, useRef } from 'react';

// 定义清理Hook
export function useCleanup(cleanupFn) {
  // 使用useRef Hook保存清理函数
  const cleanupRef = useRef(cleanupFn);

  // 更新清理函数引用
  useEffect(() => {
    cleanupRef.current = cleanupFn;
  }, [cleanupFn]);

  // 组件卸载时执行清理
  useEffect(() => {
    return () => {
      if (cleanupRef.current) {
        cleanupRef.current();
      }
    };
  }, []);
}

// 定义事件监听器Hook
export function useEventListener(eventName, handler, element = window) {
  // 使用useRef Hook保存处理函数
  const handlerRef = useRef(handler);

  // 更新处理函数引用
  useEffect(() => {
    handlerRef.current = handler;
  }, [handler]);

  // 添加和移除事件监听器
  useEffect(() => {
    const eventListener = (event) => {
      handlerRef.current(event);
    };

    if (element && element.addEventListener) {
      element.addEventListener(eventName, eventListener);
      
      // 返回清理函数,移除事件监听器
      return () => {
        element.removeEventListener(eventName, eventListener);
      };
    }
  }, [eventName, element]);
}

// 定义定时器Hook
export function useInterval(callback, delay) {
  // 使用useRef Hook保存回调函数
  const callbackRef = useRef(callback);

  // 更新回调函数引用
  useEffect(() => {
    callbackRef.current = callback;
  }, [callback]);

  // 设置和清理定时器
  useEffect(() => {
    if (delay !== null) {
      const interval = setInterval(() => {
        callbackRef.current();
      }, delay);

      // 返回清理函数,清除定时器
      return () => clearInterval(interval);
    }
  }, [delay]);
}

// 定义超时Hook
export function useTimeout(callback, delay) {
  // 使用useRef Hook保存回调函数
  const callbackRef = useRef(callback);

  // 更新回调函数引用
  useEffect(() => {
    callbackRef.current = callback;
  }, [callback]);

  // 设置和清理超时
  useEffect(() => {
    if (delay !== null) {
      const timeout = setTimeout(() => {
        callbackRef.current();
      }, delay);

      // 返回清理函数,清除超时
      return () => clearTimeout(timeout);
    }
  }, [delay]);
}

4.2 组件清理示例

完整清理示例(详细注释版)
// src/components/CleanupExample.js
// 导入React、useState、useEffect、useRef
import React, { useState, useEffect, useRef } from 'react';
// 导入清理Hooks
import { useEventListener, useInterval, useTimeout } from '../hooks/useCleanup';

// 定义需要清理的组件
function CleanupExample() {
  // 使用useState Hook管理状态
  const [count, setCount] = useState(0);
  const [mousePosition, setMousePosition] = useState({ x: 0, y: 0 });
  const [isVisible, setIsVisible] = useState(true);
  
  // 使用useRef Hook保存订阅引用
  const subscriptionRef = useRef(null);
  
  // 使用useRef Hook保存WebSocket引用
  const wsRef = useRef(null);

  // 使用useEventListener Hook监听鼠标移动
  useEventListener('mousemove', (e) => {
    setMousePosition({ x: e.clientX, y: e.clientY });
  });

  // 使用useInterval Hook设置定时器
  useInterval(() => {
    setCount(prev => prev + 1);
  }, 1000);

  // 模拟订阅清理
  useEffect(() => {
    // 模拟创建订阅
    subscriptionRef.current = {
      unsubscribe: () => {
        console.log('Subscription unsubscribed');
      }
    };

    // 返回清理函数
    return () => {
      if (subscriptionRef.current) {
        subscriptionRef.current.unsubscribe();
        subscriptionRef.current = null;
      }
    };
  }, []);

  // 模拟WebSocket连接清理
  useEffect(() => {
    // 模拟WebSocket连接
    wsRef.current = {
      close: () => {
        console.log('WebSocket closed');
      }
    };

    // 返回清理函数
    return () => {
      if (wsRef.current) {
        wsRef.current.close();
        wsRef.current = null;
      }
    };
  }, []);

  // 处理切换可见性
  const handleToggleVisibility = () => {
    setIsVisible(prev => !prev);
  };

  return (
    <div>
      <h2>Cleanup Example</h2>
      
      <div>
        <p>Count: {count}</p>
        <p>Mouse Position: {mousePosition.x}, {mousePosition.y}</p>
        <p>Component is {isVisible ? 'visible' : 'hidden'}</p>
      </div>
      
      <button onClick={handleToggleVisibility}>
        Toggle Visibility
      </button>
      
      {isVisible && (
        <div>
          <h3>Visible Content</h3>
          <p>This content will be cleaned up when hidden.</p>
        </div>
      )}
    </div>
  );
}

// 导出组件
export default CleanupExample;

第五部分:实践项目(详细注释版)

项目:高性能数据表格

主应用组件(详细注释版)
// src/App.js
// 导入React
import React from 'react';
// 导入组件
import OptimizedDataTable from './components/OptimizedDataTable';
// 导入样式
import './App.css';

// 定义主应用组件
function App() {
  // 生成大量数据
  const generateData = (count) => {
    return Array.from({ length: count }, (_, i) => ({
      id: i + 1,
      name: `Item ${i + 1}`,
      category: ['Category A', 'Category B', 'Category C'][i % 3],
      price: Math.floor(Math.random() * 1000),
      status: ['Active', 'Inactive', 'Pending'][i % 3],
      date: new Date(Date.now() - Math.random() * 10000000000).toISOString(),
      description: `This is a description for item ${i + 1}`
    }));
  };

  const data = generateData(10000);

  return (
    <div className="App">
      <header className="App-header">
        <h1>High Performance Data Table</h1>
        <p>Total items: {data.length}</p>
      </header>
      
      <main>
        <OptimizedDataTable data={data} />
      </main>
    </div>
  );
}

// 导出App组件
export default App;
优化的数据表格组件(详细注释版)
// src/components/OptimizedDataTable.js
// 导入React、useState、useMemo、useCallback、memo
import React, { useState, useMemo, useCallback, memo } from 'react';
// 导入虚拟滚动组件
import VirtualizedList from './VirtualizedList';

// 定义表格单元格组件
// 使用memo优化
const TableCell = memo(function TableCell({ value, type = 'text' }) {
  // 根据类型格式化值
  const formatValue = (val, valType) => {
    switch (valType) {
      case 'number':
        return new Intl.NumberFormat('en-US', {
          style: 'currency',
          currency: 'USD'
        }).format(val);
      case 'date':
        return new Date(val).toLocaleDateString();
      case 'status':
        return (
          <span className={`status status-${val.toLowerCase()}`}>
            {val}
          </span>
        );
      default:
        return val;
    }
  };

  return (
    <td className="table-cell">
      {formatValue(value, type)}
    </td>
  );
});

// 定义表格行组件
// 使用memo优化,自定义比较函数
const TableRow = memo(function TableRow({ 
  row, 
  columns, 
  isSelected, 
  onSelect, 
  onToggle 
}) {
  // 处理行点击
  const handleRowClick = useCallback(() => {
    onSelect(row.id);
  }, [row.id, onSelect]);

  // 处理复选框点击
  const handleCheckboxClick = useCallback((e) => {
    e.stopPropagation();
    onToggle(row.id);
  }, [row.id, onToggle]);

  return (
    <tr 
      className={`table-row ${isSelected ? 'selected' : ''}`}
      onClick={handleRowClick}
    >
      <td className="table-cell">
        <input
          type="checkbox"
          checked={isSelected}
          onChange={handleCheckboxClick}
        />
      </td>
      {columns.map(column => (
        <TableCell
          key={column.key}
          value={row[column.key]}
          type={column.type}
        />
      ))}
    </tr>
  );
}, (prevProps, nextProps) => {
  // 自定义比较函数
  // 只有当row、isSelected变化时才重新渲染
  return prevProps.row.id === nextProps.row.id &&
         prevProps.isSelected === nextProps.isSelected &&
         JSON.stringify(prevProps.row) === JSON.stringify(nextProps.row);
});

// 定义表格头部组件
// 使用memo优化
const TableHeader = memo(function TableHeader({ 
  columns, 
  sortColumn, 
  sortDirection, 
  onSort 
}) {
  // 处理排序
  const handleSort = useCallback((columnKey) => {
    onSort(columnKey);
  }, [onSort]);

  return (
    <thead>
      <tr>
        <th className="table-header">
          <input type="checkbox" />
        </th>
        {columns.map(column => (
          <th
            key={column.key}
            className="table-header"
            onClick={() => handleSort(column.key)}
          >
            {column.label}
            {sortColumn === column.key && (
              <span className="sort-indicator">
                {sortDirection === 'asc' ? '↑' : '↓'}
              </span>
            )}
          </th>
        ))}
      </tr>
    </thead>
  );
});

// 定义优化的数据表格组件
function OptimizedDataTable({ data }) {
  // 使用useState Hook管理状态
  const [sortColumn, setSortColumn] = useState(null);
  const [sortDirection, setSortDirection] = useState('asc');
  const [filter, setFilter] = useState('');
  const [selectedRows, setSelectedRows] = useState([]);
  const [pageSize, setPageSize] = useState(50);

  // 定义列配置
  const columns = useMemo(() => [
    { key: 'name', label: 'Name', type: 'text' },
    { key: 'category', label: 'Category', type: 'text' },
    { key: 'price', label: 'Price', type: 'number' },
    { key: 'status', label: 'Status', type: 'status' },
    { key: 'date', label: 'Date', type: 'date' }
  ], []);

  // 使用useMemo Hook记忆化过滤和排序后的数据
  const processedData = useMemo(() => {
    let filtered = data;

    // 应用过滤器
    if (filter.trim()) {
      const filterLower = filter.toLowerCase();
      filtered = filtered.filter(item =>
        Object.values(item).some(value =>
          String(value).toLowerCase().includes(filterLower)
        )
      );
    }

    // 应用排序
    if (sortColumn) {
      filtered = [...filtered].sort((a, b) => {
        const aValue = a[sortColumn];
        const bValue = b[sortColumn];
        
        if (aValue < bValue) return sortDirection === 'asc' ? -1 : 1;
        if (aValue > bValue) return sortDirection === 'asc' ? 1 : -1;
        return 0;
      });
    }

    return filtered;
  }, [data, filter, sortColumn, sortDirection]);

  // 使用useCallback Hook记忆化函数
  const handleSort = useCallback((columnKey) => {
    if (sortColumn === columnKey) {
      setSortDirection(prev => prev === 'asc' ? 'desc' : 'asc');
    } else {
      setSortColumn(columnKey);
      setSortDirection('asc');
    }
  }, [sortColumn]);

  // 使用useCallback Hook记忆化函数
  const handleSelect = useCallback((rowId) => {
    setSelectedRows(prev =>
      prev.includes(rowId)
        ? prev.filter(id => id !== rowId)
        : [...prev, rowId]
    );
  }, []);

  // 使用useCallback Hook记忆化函数
  const handleToggle = useCallback((rowId) => {
    handleSelect(rowId);
  }, [handleSelect]);

  // 使用useCallback Hook记忆化函数
  const handleFilterChange = useCallback((e) => {
    setFilter(e.target.value);
  }, []);

  return (
    <div className="optimized-data-table">
      <div className="table-controls">
        <input
          type="text"
          placeholder="Search..."
          value={filter}
          onChange={handleFilterChange}
          className="search-input"
        />
        
        <select
          value={pageSize}
          onChange={(e) => setPageSize(Number(e.target.value))}
          className="page-size-select"
        >
          <option value={25}>25 per page</option>
          <option value={50}>50 per page</option>
          <option value={100}>100 per page</option>
          <option value={200}>200 per page</option>
        </select>
        
        <div className="selected-info">
          Selected: {selectedRows.length} / {processedData.length}
        </div>
      </div>
      
      <div className="table-container">
        <table className="data-table">
          <TableHeader
            columns={columns}
            sortColumn={sortColumn}
            sortDirection={sortDirection}
            onSort={handleSort}
          />
          <tbody>
            {processedData.slice(0, pageSize).map(row => (
              <TableRow
                key={row.id}
                row={row}
                columns={columns}
                isSelected={selectedRows.includes(row.id)}
                onSelect={handleSelect}
                onToggle={handleToggle}
              />
            ))}
          </tbody>
        </table>
      </div>
      
      <div className="table-footer">
        <p>Showing {Math.min(pageSize, processedData.length)} of {processedData.length} items</p>
      </div>
    </div>
  );
}

// 导出组件
export default OptimizedDataTable;
样式文件(详细注释版)
/* src/App.css */
/* 应用主容器样式 */
.App {
  min-height: 100vh;
  font-family: Arial, sans-serif;
  background-color: #f5f5f5;
}

/* 应用头部样式 */
.App-header {
  background-color: #333;
  color: white;
  padding: 2rem;
  text-align: center;
}

.App-header h1 {
  margin: 0 0 1rem 0;
  font-size: 2rem;
}

.App-header p {
  margin: 0;
  font-size: 1.2rem;
  opacity: 0.8;
}

/* 主要内容区域 */
main {
  max-width: 1400px;
  margin: 0 auto;
  padding: 2rem;
}

/* 优化的数据表格样式 */
.optimized-data-table {
  background-color: white;
  border-radius: 8px;
  box-shadow: 0 2px 4px rgba(0,0,0,0.1);
  overflow: hidden;
}

.table-controls {
  display: flex;
  align-items: center;
  gap: 1rem;
  padding: 1rem;
  background-color: #f8f9fa;
  border-bottom: 1px solid #dee2e6;
}

.search-input {
  flex: 1;
  padding: 0.5rem;
  border: 1px solid #ddd;
  border-radius: 4px;
  font-size: 1rem;
}

.page-size-select {
  padding: 0.5rem;
  border: 1px solid #ddd;
  border-radius: 4px;
  font-size: 1rem;
}

.selected-info {
  font-weight: bold;
  color: #666;
}

.table-container {
  max-height: 600px;
  overflow-y: auto;
}

.data-table {
  width: 100%;
  border-collapse: collapse;
}

.table-header {
  background-color: #f8f9fa;
  padding: 1rem;
  text-align: left;
  font-weight: bold;
  border-bottom: 2px solid #dee2e6;
  cursor: pointer;
  user-select: none;
  position: sticky;
  top: 0;
  z-index: 10;
}

.table-header:hover {
  background-color: #e9ecef;
}

.sort-indicator {
  margin-left: 0.5rem;
  color: #007bff;
}

.table-row {
  border-bottom: 1px solid #dee2e6;
  transition: background-color 0.2s;
  cursor: pointer;
}

.table-row:hover {
  background-color: #f8f9fa;
}

.table-row.selected {
  background-color: #e3f2fd;
}

.table-cell {
  padding: 1rem;
  border-right: 1px solid #f0f0f0;
}

.table-cell:last-child {
  border-right: none;
}

.status {
  padding: 0.25rem 0.5rem;
  border-radius: 4px;
  font-size: 0.875rem;
  font-weight: 500;
}

.status-active {
  background-color: #d4edda;
  color: #155724;
}

.status-inactive {
  background-color: #f8d7da;
  color: #721c24;
}

.status-pending {
  background-color: #fff3cd;
  color: #856404;
}

.table-footer {
  padding: 1rem;
  background-color: #f8f9fa;
  border-top: 1px solid #dee2e6;
  text-align: center;
  color: #666;
}

/* 虚拟滚动样式 */
.virtualized-list {
  border: 1px solid #dee2e6;
  border-radius: 4px;
}

/* 懒加载图片样式 */
.lazy-image-container {
  position: relative;
  width: 100%;
  height: 100%;
  overflow: hidden;
}

.lazy-image-placeholder {
  position: absolute;
  top: 0;
  left: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
}

.lazy-image {
  width: 100%;
  height: 100%;
  object-fit: cover;
}

.lazy-image.loaded {
  opacity: 1;
}

.image-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
  gap: 1rem;
  padding: 1rem;
}

.grid-image {
  width: 100%;
  height: 200px;
  border-radius: 8px;
  overflow: hidden;
}

/* 加载状态样式 */
.loading-fallback,
.page-loader {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  padding: 2rem;
  min-height: 200px;
}

.spinner {
  width: 40px;
  height: 40px;
  border: 4px solid #f3f3f3;
  border-top: 4px solid #007bff;
  border-radius: 50%;
  animation: spin 1s linear infinite;
}

@keyframes spin {
  0% { transform: rotate(0deg); }
  100% { transform: rotate(360deg); }
}

/* 响应式设计 */
@media (max-width: 768px) {
  .table-controls {
    flex-direction: column;
    align-items: stretch;
  }
  
  .search-input {
    width: 100%;
  }
  
  .table-container {
    overflow-x: auto;
  }
  
  .data-table {
    min-width: 600px;
  }
}

练习题目

基础练习

  1. 性能优化基础练习
// 练习1:创建一个使用memo优化的组件
// 实现:父子组件通信,使用memo防止不必要的重新渲染
// 包含:性能监控、渲染次数统计

// 练习2:实现useMemo和useCallback优化
// 实现:昂贵的计算和函数记忆化
// 包含:性能对比、优化前后差异
  1. 渲染优化练习
// 练习3:实现虚拟滚动列表
// 实现:处理大量数据的高性能列表
// 包含:虚拟滚动、懒加载、性能监控

// 练习4:实现图片懒加载
// 实现:IntersectionObserver API、占位符、加载状态
// 包含:性能优化、用户体验提升

进阶练习

  1. 内存管理练习
// 练习5:实现完整的内存管理
// 实现:事件监听器清理、定时器清理、订阅清理
// 包含:内存泄漏检测、性能分析

// 练习6:构建高性能数据表格
// 实现:虚拟滚动、排序、过滤、分页
// 包含:性能优化、用户体验优化
  1. 综合应用练习
// 练习7:优化现有React应用
// 实现:识别性能问题、应用优化技巧
// 包含:性能分析、优化报告、最佳实践
Logo

Agent 垂直技术社区,欢迎活跃、内容共建。

更多推荐