Appearance
性能优化清单 - React应用性能优化全面指南
本文档提供React应用性能优化的完整检查清单,涵盖各个层面的优化策略。
1. 组件渲染优化
1.1 避免不必要的重渲染
tsx
// ✅ 使用React.memo
const MemoizedComponent = React.memo(function Component({ data }) {
return <div>{data}</div>;
});
// ✅ 自定义比较函数
const MemoizedList = React.memo(
function List({ items }) {
return items.map(item => <div key={item.id}>{item.name}</div>);
},
(prevProps, nextProps) => {
return prevProps.items.length === nextProps.items.length &&
prevProps.items.every((item, i) => item.id === nextProps.items[i].id);
}
);
// ✅ useMemo缓存昂贵计算
const sortedData = useMemo(() =>
data.sort((a, b) => a.value - b.value),
[data]
);
// ✅ useCallback稳定函数引用
const handleClick = useCallback(() => {
console.log(data);
}, [data]);
// ✅ 状态下放
// ❌ 不好
function Parent() {
const [text, setText] = useState('');
return (
<div>
<input value={text} onChange={e => setText(e.target.value)} />
<ExpensiveChild /> {/* text变化时不必要渲染 */}
</div>
);
}
// ✅ 好
function Parent() {
return (
<div>
<TextInput />
<ExpensiveChild />
</div>
);
}
function TextInput() {
const [text, setText] = useState('');
return <input value={text} onChange={e => setText(e.target.value)} />;
}1.2 优化列表渲染
tsx
// ✅ 使用key
items.map(item => <Item key={item.id} data={item} />);
// ✅ 虚拟滚动 (react-window)
import { FixedSizeList } from 'react-window';
function VirtualList({ items }) {
return (
<FixedSizeList
height={600}
itemCount={items.length}
itemSize={50}
width="100%"
>
{({ index, style }) => (
<div style={style}>{items[index]}</div>
)}
</FixedSizeList>
);
}
// ✅ 分页/无限滚动
function PaginatedList({ items, pageSize = 20 }) {
const [page, setPage] = useState(1);
const paginatedItems = items.slice(0, page * pageSize);
return (
<div>
{paginatedItems.map(item => <Item key={item.id} data={item} />)}
{page * pageSize < items.length && (
<button onClick={() => setPage(p => p + 1)}>Load More</button>
)}
</div>
);
}2. 代码分割
2.1 路由级别
tsx
// ✅ 懒加载路由组件
import { lazy, Suspense } from 'react';
import { BrowserRouter, Routes, Route } from 'react-router-dom';
const Home = lazy(() => import('./pages/Home'));
const About = lazy(() => import('./pages/About'));
const Dashboard = lazy(() => import('./pages/Dashboard'));
function App() {
return (
<BrowserRouter>
<Suspense fallback={<Loading />}>
<Routes>
<Route path="/" element={<Home />} />
<Route path="/about" element={<About />} />
<Route path="/dashboard" element={<Dashboard />} />
</Routes>
</Suspense>
</BrowserRouter>
);
}2.2 组件级别
tsx
// ✅ 按需加载大型组件
const HeavyChart = lazy(() => import('./HeavyChart'));
function Dashboard() {
const [showChart, setShowChart] = useState(false);
return (
<div>
<button onClick={() => setShowChart(true)}>Show Chart</button>
{showChart && (
<Suspense fallback={<Spinner />}>
<HeavyChart />
</Suspense>
)}
</div>
);
}2.3 库级别
tsx
// ✅ 动态导入大型库
const loadChartLibrary = async () => {
const Chart = await import('chart.js');
return Chart;
};
function ChartComponent() {
const [Chart, setChart] = useState(null);
useEffect(() => {
loadChartLibrary().then(setChart);
}, []);
if (!Chart) return <Spinner />;
return <Chart.Line data={data} />;
}3. 图片优化
3.1 懒加载图片
tsx
// ✅ 使用Intersection Observer
function LazyImage({ src, alt }) {
const [imageSrc, setImageSrc] = useState(null);
const imgRef = useRef();
useEffect(() => {
const observer = new IntersectionObserver(
([entry]) => {
if (entry.isIntersecting) {
setImageSrc(src);
observer.disconnect();
}
},
{ threshold: 0.1 }
);
if (imgRef.current) {
observer.observe(imgRef.current);
}
return () => observer.disconnect();
}, [src]);
return (
<img
ref={imgRef}
src={imageSrc || 'placeholder.jpg'}
alt={alt}
/>
);
}3.2 响应式图片
tsx
// ✅ 使用srcset
function ResponsiveImage({ src, alt }) {
return (
<img
src={src}
srcSet={`
${src}?w=400 400w,
${src}?w=800 800w,
${src}?w=1200 1200w
`}
sizes="(max-width: 600px) 400px, (max-width: 900px) 800px, 1200px"
alt={alt}
/>
);
}3.3 Next.js Image组件
tsx
// ✅ 使用Next.js优化图片
import Image from 'next/image';
function OptimizedImage() {
return (
<Image
src="/hero.jpg"
alt="Hero"
width={800}
height={600}
placeholder="blur"
priority
/>
);
}4. 网络优化
4.1 数据缓存
tsx
// ✅ 使用React Query缓存
import { useQuery } from '@tanstack/react-query';
function Users() {
const { data, isLoading } = useQuery({
queryKey: ['users'],
queryFn: fetchUsers,
staleTime: 5 * 60 * 1000, // 5分钟
cacheTime: 10 * 60 * 1000 // 10分钟
});
if (isLoading) return <Spinner />;
return <UserList users={data} />;
}4.2 请求合并
tsx
// ✅ 使用DataLoader模式
import DataLoader from 'dataloader';
const userLoader = new DataLoader(async (ids) => {
const users = await fetchUsersByIds(ids);
return ids.map(id => users.find(u => u.id === id));
});
// 多个组件请求相同用户时会自动合并
function User({ userId }) {
const [user, setUser] = useState(null);
useEffect(() => {
userLoader.load(userId).then(setUser);
}, [userId]);
return <div>{user?.name}</div>;
}4.3 预取数据
tsx
// ✅ 鼠标悬停时预取
import { useQueryClient } from '@tanstack/react-query';
function ProductCard({ product }) {
const queryClient = useQueryClient();
const prefetchDetails = () => {
queryClient.prefetchQuery({
queryKey: ['product', product.id],
queryFn: () => fetchProductDetails(product.id)
});
};
return (
<div onMouseEnter={prefetchDetails}>
<Link to={`/products/${product.id}`}>
{product.name}
</Link>
</div>
);
}5. 并发特性
5.1 useTransition
tsx
// ✅ 标记非紧急更新
import { useState, useTransition } from 'react';
function SearchComponent() {
const [query, setQuery] = useState('');
const [results, setResults] = useState([]);
const [isPending, startTransition] = useTransition();
const handleChange = (e) => {
const value = e.target.value;
setQuery(value); // 紧急更新,立即响应
startTransition(() => {
// 非紧急更新,可以延迟
const searchResults = performExpensiveSearch(value);
setResults(searchResults);
});
};
return (
<div>
<input value={query} onChange={handleChange} />
{isPending && <Spinner />}
<SearchResults results={results} />
</div>
);
}5.2 useDeferredValue
tsx
// ✅ 延迟值更新
import { useState, useDeferredValue, useMemo } from 'react';
function SearchResults({ query }) {
const deferredQuery = useDeferredValue(query);
const results = useMemo(() => {
return performExpensiveSearch(deferredQuery);
}, [deferredQuery]);
return <ResultList results={results} />;
}
function SearchApp() {
const [query, setQuery] = useState('');
return (
<div>
<input
value={query}
onChange={e => setQuery(e.target.value)}
/>
<SearchResults query={query} />
</div>
);
}6. Bundle优化
6.1 Tree Shaking
tsx
// ✅ 使用命名导入
import { debounce } from 'lodash-es';
// ❌ 避免默认导入整个库
import _ from 'lodash';6.2 动态导入
tsx
// ✅ 按需加载
const handleExport = async () => {
const { exportToExcel } = await import('./exportUtils');
exportToExcel(data);
};6.3 分析Bundle
bash
# 使用webpack-bundle-analyzer
npm install --save-dev webpack-bundle-analyzer
# 或者使用vite-plugin-visualizer
npm install --save-dev rollup-plugin-visualizer7. Web Vitals优化
7.1 LCP优化
tsx
// ✅ 预加载关键资源
<link rel="preload" as="image" href="/hero.jpg" />
// ✅ 优化图片
// 使用WebP格式、懒加载、响应式图片
// ✅ 服务端渲染
// 使用Next.js等框架的SSR功能7.2 FID优化
tsx
// ✅ 代码分割减少主线程阻塞
// ✅ 使用Web Workers处理计算密集任务
const worker = new Worker('/heavy-computation-worker.js');
worker.postMessage({ data });
worker.onmessage = (e) => {
setResult(e.data);
};7.3 CLS优化
tsx
// ✅ 为图片设置width和height
<img src="/image.jpg" width={800} height={600} alt="Image" />
// ✅ 为动态内容预留空间
<div style={{ minHeight: '200px' }}>
{loading ? <Skeleton /> : <Content />}
</div>
// ✅ 使用CSS aspect-ratio
.image-container {
aspect-ratio: 16 / 9;
}8. 性能监控
8.1 Web Vitals测量
tsx
import { getCLS, getFID, getFCP, getLCP, getTTFB } from 'web-vitals';
function reportWebVitals(metric) {
console.log(metric);
// 发送到分析服务
sendToAnalytics(metric);
}
getCLS(reportWebVitals);
getFID(reportWebVitals);
getFCP(reportWebVitals);
getLCP(reportWebVitals);
getTTFB(reportWebVitals);8.2 React Profiler
tsx
import { Profiler } from 'react';
function onRenderCallback(
id,
phase,
actualDuration,
baseDuration,
startTime,
commitTime
) {
console.log({ id, phase, actualDuration });
}
function App() {
return (
<Profiler id="App" onRender={onRenderCallback}>
<MainContent />
</Profiler>
);
}9. 性能优化清单
typescript
const performanceChecklist = {
组件优化: [
'✅ 使用React.memo避免不必要渲染',
'✅ 使用useMemo缓存昂贵计算',
'✅ 使用useCallback稳定函数引用',
'✅ 避免内联对象/数组/函数',
'✅ 状态下放到需要的组件',
'✅ 使用children prop避免重渲染'
],
列表优化: [
'✅ 使用正确的key',
'✅ 大列表使用虚拟滚动',
'✅ 实现分页或无限滚动',
'✅ 优化列表项渲染'
],
代码分割: [
'✅ 路由级别懒加载',
'✅ 组件级别懒加载',
'✅ 第三方库按需导入',
'✅ 使用动态import'
],
资源优化: [
'✅ 图片懒加载',
'✅ 使用WebP格式',
'✅ 响应式图片',
'✅ 使用CDN'
],
网络优化: [
'✅ 启用HTTP/2',
'✅ 使用缓存策略',
'✅ 数据预取',
'✅ 请求合并'
],
并发特性: [
'✅ 使用useTransition',
'✅ 使用useDeferredValue',
'✅ 利用Suspense',
'✅ 利用React 18自动批量更新'
],
构建优化: [
'✅ Tree Shaking',
'✅ 代码压缩',
'✅ Gzip/Brotli压缩',
'✅ 分析Bundle大小'
],
监控: [
'✅ 测量Web Vitals',
'✅ 使用React Profiler',
'✅ 设置性能预算',
'✅ 持续监控'
]
};10. 性能预算
javascript
// 设置性能预算
const performanceBudget = {
FCP: 1.8, // 秒
LCP: 2.5,
FID: 0.1,
CLS: 0.1,
TTI: 3.8,
bundleSize: {
main: 200, // KB
vendor: 500,
total: 1000
}
};
// 在CI/CD中检查
if (metrics.LCP > performanceBudget.LCP) {
throw new Error('LCP超出预算');
}11. 总结
性能优化是一个持续的过程:
- 测量: 使用工具测量关键指标
- 分析: 找出性能瓶颈
- 优化: 应用优化策略
- 验证: 确认优化效果
- 监控: 持续监控性能
遵循本清单可以系统地优化React应用性能,提供更好的用户体验。