React 性能优化完全指南
性能优化是构建优秀 React 应用的关键。本文将介绍一系列实用的优化技巧。
1. 使用 React.memo 避免不必要的重渲染
import React, { memo } from 'react';
interface Props {
title: string;
count: number;
}
// 只有当 props 改变时才重新渲染
const ExpensiveComponent = memo<Props>(({ title, count }) => {
console.log('Rendering ExpensiveComponent');
return (
<div>
<h2>{title}</h2>
<p>Count: {count}</p>
</div>
);
});
// 自定义比较函数
const ExpensiveComponentWithCustomCompare = memo<Props>(
({ title, count }) => {
return (
<div>
<h2>{title}</h2>
<p>Count: {count}</p>
</div>
);
},
(prevProps, nextProps) => {
// 返回 true 表示不需要重新渲染
return prevProps.count === nextProps.count;
},
);
2. useMemo 和 useCallback
useMemo - 缓存计算结果
import { useMemo } from 'react';
function DataList({ items, filter }) {
// 只有当 items 或 filter 改变时才重新计算
const filteredItems = useMemo(() => {
console.log('Filtering items...');
return items.filter((item) => item.category === filter);
}, [items, filter]);
return (
<ul>
{filteredItems.map((item) => (
<li key={item.id}>{item.name}</li>
))}
</ul>
);
}
useCallback - 缓存函数引用
import { useCallback, useState } from 'react';
function ParentComponent() {
const [count, setCount] = useState(0);
// 缓存函数引用,避免子组件不必要的重渲染
const handleClick = useCallback(() => {
setCount((c) => c + 1);
}, []); // 空依赖数组,函数引用永不改变
return (
<div>
<p>Count: {count}</p>
<ChildComponent onClick={handleClick} />
</div>
);
}
const ChildComponent = memo(({ onClick }) => {
console.log('Child rendered');
return <button onClick={onClick}>Increment</button>;
});
3. 代码分割和懒加载
import { lazy, Suspense } from 'react';
// 懒加载组件
const HeavyComponent = lazy(() => import('./HeavyComponent'));
function App() {
return (
<Suspense fallback={<div>Loading...</div>}>
<HeavyComponent />
</Suspense>
);
}
4. 虚拟列表
对于长列表,使用虚拟滚动:
import { FixedSizeList } from 'react-window';
function VirtualList({ items }) {
const Row = ({ index, style }) => <div style={style}>{items[index].name}</div>;
return (
<FixedSizeList height={600} itemCount={items.length} itemSize={50} width="100%">
{Row}
</FixedSizeList>
);
}
5. 图片优化
import Image from 'next/image';
// Next.js 自动优化图片
<Image
src="/large-image.jpg"
alt="Description"
width={800}
height={600}
loading="lazy" // 懒加载
placeholder="blur" // 模糊占位符
/>
// 原生 React 懒加载
<img
src="/image.jpg"
alt="Description"
loading="lazy"
decoding="async"
/>
6. 避免内联对象和函数
// ❌ 不推荐 - 每次渲染都创建新对象
function BadComponent() {
return <Child style={{ margin: 10 }} onClick={() => console.log('clicked')} />;
}
// ✅ 推荐
function GoodComponent() {
const style = useMemo(() => ({ margin: 10 }), []);
const handleClick = useCallback(() => console.log('clicked'), []);
return <Child style={style} onClick={handleClick} />;
}
7. 使用 Web Workers 处理密集计算
// worker.ts
self.onmessage = (e) => {
const result = heavyCalculation(e.data);
self.postMessage(result);
};
// Component.tsx
import { useEffect, useState } from 'react';
function Component() {
const [result, setResult] = useState(null);
useEffect(() => {
const worker = new Worker(new URL('./worker.ts', import.meta.url));
worker.onmessage = (e) => {
setResult(e.data);
};
worker.postMessage({ data: largeDataset });
return () => worker.terminate();
}, []);
return <div>{result}</div>;
}
8. 使用 startTransition 标记非紧急更新
import { startTransition, useState } from 'react';
function SearchComponent() {
const [input, setInput] = useState('');
const [results, setResults] = useState([]);
const handleChange = (e) => {
// 紧急更新:立即更新输入框
setInput(e.target.value);
// 非紧急更新:可以延迟的搜索结果
startTransition(() => {
const filtered = heavySearch(e.target.value);
setResults(filtered);
});
};
return (
<>
<input value={input} onChange={handleChange} />
<ResultsList results={results} />
</>
);
}
9. 性能监控
import { useEffect } from 'react';
function PerformanceMonitor({ children }) {
useEffect(() => {
// 监控 Core Web Vitals
if ('web-vitals' in window) {
import('web-vitals').then(({ getCLS, getFID, getFCP, getLCP, getTTFB }) => {
getCLS(console.log);
getFID(console.log);
getFCP(console.log);
getLCP(console.log);
getTTFB(console.log);
});
}
}, []);
return children;
}
10. 生产环境构建优化
// next.config.js
module.exports = {
// 压缩输出
compress: true,
// 分析包大小
webpack: (config, { isServer }) => {
if (!isServer) {
config.optimization.splitChunks = {
chunks: 'all',
cacheGroups: {
default: false,
vendors: false,
vendor: {
name: 'vendor',
chunks: 'all',
test: /node_modules/,
},
},
};
}
return config;
},
};
性能检查清单
- 使用 React DevTools Profiler 分析组件性能
- 检查不必要的重渲染
- 优化大型列表渲染
- 实现代码分割和懒加载
- 优化图片加载
- 使用生产环境构建
- 启用 Gzip/Brotli 压缩
- 配置 CDN
- 实现适当的缓存策略
总结
性能优化是一个持续的过程,需要:
- 🔍 测量:使用工具识别性能瓶颈
- ⚡ 优化:应用合适的优化技巧
- 📊 监控:持续跟踪性能指标
- 🔄 迭代:根据数据不断改进
记住:过早优化是万恶之源。先让代码工作,然后再优化真正的瓶颈。
你还有哪些 React 性能优化的技巧?欢迎分享!