性能优化 #
一、性能优化概述 #
1.1 为什么需要性能优化 #
React 应用可能遇到的性能问题:
| 问题 | 表现 |
|---|---|
| 不必要渲染 | 组件频繁重新渲染 |
| 大型列表卡顿 | 滚动不流畅 |
| 首屏加载慢 | 白屏时间长 |
| 内存泄漏 | 页面越来越慢 |
1.2 性能检测工具 #
javascript
// React DevTools Profiler
// 1. 安装React DevTools浏览器扩展
// 2. 打开Profiler面板
// 3. 点击录制,操作页面
// 4. 分析渲染性能
// 使用React内置Profiler
import { Profiler } from 'react';
function App() {
const onRender = (id, phase, actualDuration) => {
console.log(`${id} ${phase} took ${actualDuration}ms`);
};
return (
<Profiler id="App" onRender={onRender}>
<Component />
</Profiler>
);
}
二、避免不必要渲染 #
2.1 React.memo #
javascript
// 默认情况下,父组件更新,子组件也会更新
const Child = React.memo(function Child({ name }) {
console.log('Child render');
return <div>{name}</div>;
});
// 自定义比较函数
const Child = React.memo(
function Child({ user }) {
return <div>{user.name}</div>;
},
(prevProps, nextProps) => {
return prevProps.user.id === nextProps.user.id;
}
);
2.2 useMemo #
javascript
function ExpensiveComponent({ list, filter }) {
// 缓存计算结果
const filteredList = useMemo(() => {
console.log('Computing...');
return list.filter(item => item.includes(filter));
}, [list, filter]);
// 缓存复杂对象
const config = useMemo(() => ({
options: list.map(item => ({ label: item, value: item })),
settings: { multi: true }
}), [list]);
return <List data={filteredList} config={config} />;
}
2.3 useCallback #
javascript
function Parent() {
const [count, setCount] = useState(0);
const [items, setItems] = useState([]);
// 缓存回调函数
const handleClick = useCallback(() => {
console.log('clicked');
}, []);
// 带依赖的回调
const handleAdd = useCallback((item) => {
setItems(prev => [...prev, item]);
}, []);
return (
<div>
<button onClick={() => setCount(count + 1)}>{count}</button>
<Child onClick={handleClick} onAdd={handleAdd} />
</div>
);
}
const Child = React.memo(function Child({ onClick, onAdd }) {
console.log('Child render');
return (
<div>
<button onClick={onClick}>Click</button>
<button onClick={() => onAdd('new')}>Add</button>
</div>
);
});
2.4 虚拟化长列表 #
javascript
import { FixedSizeList } from 'react-window';
function VirtualList({ items }) {
const Row = ({ index, style }) => (
<div style={style}>
{items[index].name}
</div>
);
return (
<FixedSizeList
height={400}
width={300}
itemCount={items.length}
itemSize={35}
>
{Row}
</FixedSizeList>
);
}
三、状态优化 #
3.1 状态下沉 #
javascript
// ❌ 状态在顶层,导致大量组件重新渲染
function App() {
const [value, setValue] = useState('');
return (
<div>
<Header />
<Main />
<Footer />
<input value={value} onChange={(e) => setValue(e.target.value)} />
</div>
);
}
// ✅ 状态下沉到需要的组件
function App() {
return (
<div>
<Header />
<Main />
<Footer />
<SearchInput />
</div>
);
}
function SearchInput() {
const [value, setValue] = useState('');
return <input value={value} onChange={(e) => setValue(e.target.value)} />;
}
3.2 状态提升 #
javascript
// 当多个组件需要共享状态时,提升到共同父组件
function App() {
const [selected, setSelected] = useState(null);
return (
<div>
<List selected={selected} onSelect={setSelected} />
<Detail selected={selected} />
</div>
);
}
3.3 使用Context避免prop drilling #
javascript
const UserContext = createContext();
function App() {
const [user, setUser] = useState(null);
return (
<UserContext.Provider value={{ user, setUser }}>
<Layout />
</UserContext.Provider>
);
}
function Profile() {
const { user } = useContext(UserContext);
return <div>{user?.name}</div>;
}
3.4 状态拆分 #
javascript
// ❌ 单个大状态对象
const [state, setState] = useState({
user: null,
posts: [],
comments: [],
settings: {}
});
// ✅ 拆分为独立状态
const [user, setUser] = useState(null);
const [posts, setPosts] = useState([]);
const [comments, setComments] = useState([]);
const [settings, setSettings] = useState({});
四、代码分割 #
4.1 动态import #
javascript
import { lazy, Suspense } from 'react';
// 懒加载组件
const Dashboard = lazy(() => import('./Dashboard'));
const Settings = lazy(() => import('./Settings'));
function App() {
return (
<Suspense fallback={<div>Loading...</div>}>
<Dashboard />
</Suspense>
);
}
4.2 路由级代码分割 #
javascript
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>
);
}
4.3 条件加载 #
javascript
function App() {
const [showDashboard, setShowDashboard] = useState(false);
const loadDashboard = async () => {
const module = await import('./Dashboard');
setDashboard(() => module.default);
setShowDashboard(true);
};
return (
<div>
<button onClick={loadDashboard}>Load Dashboard</button>
{showDashboard && <Dashboard />}
</div>
);
}
五、列表优化 #
5.1 使用key #
javascript
// ❌ 使用index作为key
{items.map((item, index) => <Item key={index} item={item} />)}
// ✅ 使用唯一ID作为key
{items.map(item => <Item key={item.id} item={item} />)}
5.2 避免内联函数 #
javascript
// ❌ 每次渲染创建新函数
{items.map(item => (
<Item
key={item.id}
onClick={() => handleClick(item.id)}
/>
))}
// ✅ 使用useCallback或传递item
const handleItemClick = useCallback((id) => {
console.log(id);
}, []);
{items.map(item => (
<Item
key={item.id}
id={item.id}
onClick={handleItemClick}
/>
))}
5.3 分页/虚拟滚动 #
javascript
// 分页
function PaginatedList({ items, pageSize }) {
const [page, setPage] = useState(1);
const paginatedItems = items.slice(
(page - 1) * pageSize,
page * pageSize
);
return (
<div>
{paginatedItems.map(item => (
<Item key={item.id} item={item} />
))}
<Pagination
current={page}
total={Math.ceil(items.length / pageSize)}
onChange={setPage}
/>
</div>
);
}
六、图片优化 #
6.1 懒加载图片 #
javascript
function LazyImage({ src, alt }) {
const [loaded, setLoaded] = useState(false);
const [inView, setInView] = useState(false);
const imgRef = useRef();
useEffect(() => {
const observer = new IntersectionObserver(([entry]) => {
if (entry.isIntersecting) {
setInView(true);
observer.disconnect();
}
});
if (imgRef.current) {
observer.observe(imgRef.current);
}
return () => observer.disconnect();
}, []);
return (
<div ref={imgRef}>
{inView ? (
<img
src={src}
alt={alt}
onLoad={() => setLoaded(true)}
style={{ opacity: loaded ? 1 : 0 }}
/>
) : (
<div className="placeholder" />
)}
</div>
);
}
6.2 响应式图片 #
javascript
function ResponsiveImage({ src, alt, sizes }) {
return (
<picture>
<source
media="(min-width: 1200px)"
srcSet={`${src}?w=1200 1x, ${src}?w=2400 2x`}
/>
<source
media="(min-width: 768px)"
srcSet={`${src}?w=768 1x, ${src}?w=1536 2x`}
/>
<img
src={`${src}?w=400`}
srcSet={`${src}?w=400 1x, ${src}?w=800 2x`}
alt={alt}
sizes={sizes}
loading="lazy"
/>
</picture>
);
}
七、网络优化 #
7.1 数据缓存 #
javascript
const cache = new Map();
function useFetch(url) {
const [data, setData] = useState(() => cache.get(url));
useEffect(() => {
if (cache.has(url)) {
setData(cache.get(url));
return;
}
fetch(url)
.then(res => res.json())
.then(data => {
cache.set(url, data);
setData(data);
});
}, [url]);
return data;
}
7.2 请求去重 #
javascript
const pendingRequests = new Map();
async function fetchWithDedup(url) {
if (pendingRequests.has(url)) {
return pendingRequests.get(url);
}
const promise = fetch(url).then(res => res.json());
pendingRequests.set(url, promise);
try {
const data = await promise;
return data;
} finally {
pendingRequests.delete(url);
}
}
7.3 预加载 #
javascript
function preloadImage(src) {
return new Promise((resolve) => {
const img = new Image();
img.onload = resolve;
img.src = src;
});
}
function usePreloadImages(images) {
useEffect(() => {
images.forEach(preloadImage);
}, [images]);
}
八、内存优化 #
8.1 清理副作用 #
javascript
function Component() {
useEffect(() => {
const timer = setInterval(() => {
console.log('tick');
}, 1000);
const subscription = eventEmitter.subscribe(handler);
return () => {
clearInterval(timer);
subscription.unsubscribe();
};
}, []);
}
8.2 避免内存泄漏 #
javascript
function Component() {
const [data, setData] = useState(null);
useEffect(() => {
let isMounted = true;
fetchData().then(result => {
if (isMounted) {
setData(result);
}
});
return () => {
isMounted = false;
};
}, []);
}
8.3 使用WeakMap缓存 #
javascript
const cache = new WeakMap();
function getCachedValue(obj) {
if (cache.has(obj)) {
return cache.get(obj);
}
const value = computeValue(obj);
cache.set(obj, value);
return value;
}
九、性能优化清单 #
9.1 渲染优化 #
| 优化项 | 方法 |
|---|---|
| 避免不必要渲染 | React.memo |
| 缓存计算 | useMemo |
| 缓存函数 | useCallback |
| 虚拟列表 | react-window |
9.2 加载优化 #
| 优化项 | 方法 |
|---|---|
| 代码分割 | lazy + Suspense |
| 图片懒加载 | IntersectionObserver |
| 预加载 | preload/prefetch |
9.3 状态优化 #
| 优化项 | 方法 |
|---|---|
| 状态下沉 | 状态放在最近组件 |
| 状态拆分 | 避免大状态对象 |
| Context优化 | 拆分Context |
十、总结 #
| 类别 | 优化方法 |
|---|---|
| 渲染 | memo、useMemo、useCallback |
| 列表 | key、虚拟滚动、分页 |
| 加载 | 代码分割、懒加载 |
| 网络 | 缓存、去重、预加载 |
| 内存 | 清理副作用、避免泄漏 |
核心原则:
- 避免不必要的渲染
- 合理使用缓存
- 代码分割按需加载
- 及时清理资源
最后更新:2026-03-26