React 18.x 学习计划 - 第七天:React性能优化
学习目标
- 理解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><inputtype="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 => (<ListItemkey={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)}><inputtype="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"><ListHeadertotal={items.length}selected={selectedItems.length}onSelectAll={handleSelectAll}onClearSelection={handleClearSelection}/><ListFilterfilter={filter}onFilterChange={handleFilterChange}/><div className="list-items">{filteredItems.map(item => (<ListItemkey={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 (<divref={containerRef}className="virtualized-list"style={{height: containerHeight,overflow: 'auto',position: 'relative'}}onScroll={handleScroll}>{/* 占位元素,保持总高度 */}<div style={{ height: totalHeight, position: 'relative' }}>{/* 可见项目容器 */}<divstyle={{position: 'absolute',top: offsetY,left: 0,right: 0}}>{visibleItems.map((item, index) => (<divkey={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 && (<imgsrc={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 && (<imgsrc={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) => (<LazyImagekey={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"><inputtype="checkbox"checked={isSelected}onChange={handleCheckboxClick}/></td>{columns.map(column => (<TableCellkey={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 => (<thkey={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"><inputtype="text"placeholder="Search..."value={filter}onChange={handleFilterChange}className="search-input"/><selectvalue={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"><TableHeadercolumns={columns}sortColumn={sortColumn}sortDirection={sortDirection}onSort={handleSort}/><tbody>{processedData.slice(0, pageSize).map(row => (<TableRowkey={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:创建一个使用memo优化的组件
// 实现:父子组件通信,使用memo防止不必要的重新渲染
// 包含:性能监控、渲染次数统计// 练习2:实现useMemo和useCallback优化
// 实现:昂贵的计算和函数记忆化
// 包含:性能对比、优化前后差异
- 渲染优化练习
// 练习3:实现虚拟滚动列表
// 实现:处理大量数据的高性能列表
// 包含:虚拟滚动、懒加载、性能监控// 练习4:实现图片懒加载
// 实现:IntersectionObserver API、占位符、加载状态
// 包含:性能优化、用户体验提升
进阶练习
- 内存管理练习
// 练习5:实现完整的内存管理
// 实现:事件监听器清理、定时器清理、订阅清理
// 包含:内存泄漏检测、性能分析// 练习6:构建高性能数据表格
// 实现:虚拟滚动、排序、过滤、分页
// 包含:性能优化、用户体验优化
- 综合应用练习
// 练习7:优化现有React应用
// 实现:识别性能问题、应用优化技巧
// 包含:性能分析、优化报告、最佳实践
