学习目标
- 理解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:创建一个使用memo优化的组件
// 实现:父子组件通信,使用memo防止不必要的重新渲染
// 包含:性能监控、渲染次数统计
// 练习2:实现useMemo和useCallback优化
// 实现:昂贵的计算和函数记忆化
// 包含:性能对比、优化前后差异
- 渲染优化练习
// 练习3:实现虚拟滚动列表
// 实现:处理大量数据的高性能列表
// 包含:虚拟滚动、懒加载、性能监控
// 练习4:实现图片懒加载
// 实现:IntersectionObserver API、占位符、加载状态
// 包含:性能优化、用户体验提升
进阶练习
- 内存管理练习
// 练习5:实现完整的内存管理
// 实现:事件监听器清理、定时器清理、订阅清理
// 包含:内存泄漏检测、性能分析
// 练习6:构建高性能数据表格
// 实现:虚拟滚动、排序、过滤、分页
// 包含:性能优化、用户体验优化
- 综合应用练习
// 练习7:优化现有React应用
// 实现:识别性能问题、应用优化技巧
// 包含:性能分析、优化报告、最佳实践
浙公网安备 33010602011771号