大家好,我是小杨,一个写了6年前端的老油条。今天咱们聊聊React性能优化------这个话题看似简单,但很多项目其实都藏着不少性能隐患。
你有没有遇到过这些情况?
- 页面越写越卡,交互变得迟钝
- 列表滚动时疯狂掉帧,用户体验直线下降
- 明明数据没变,组件却莫名其妙重新渲染
如果你中招了,那这篇就是为你准备的。咱们不扯虚的,直接上干货!
1. 为什么React会变慢?
React的核心是虚拟DOM(Virtual DOM) ,它通过Diff算法比对变化,然后最小化DOM操作。但如果你滥用setState
、不合理的组件设计,或者依赖项没处理好,React就会做很多不必要的计算,导致性能下降。
2. 实战优化技巧
✅ 技巧1:避免不必要的重新渲染(React.memo / PureComponent)
如果你的组件接收的props没变 ,但依然频繁渲染,可以用React.memo
(函数组件)或PureComponent
(类组件)来优化:
jsx
// 优化前:每次父组件更新,Child都会重新渲染
const Child = ({ data }) => {
console.log("Child渲染了!");
return <div>{data}</div>;
};
// 优化后:只有data变化时才重新渲染
const MemoizedChild = React.memo(({ data }) => {
console.log("MemoizedChild渲染了!");
return <div>{data}</div>;
});
适用场景:
- 纯展示型组件
- 频繁更新的父组件下的子组件
✅ 技巧2:useCallback & useMemo 缓存函数和计算值
问题 :每次父组件渲染,内联函数都会重新创建,导致子组件(即使用了React.memo
)也重新渲染。
jsx
// ❌ 每次渲染都会创建新的handleClick
const Parent = () => {
const handleClick = () => {
console.log("点击了我");
};
return <Child onClick={handleClick} />;
};
// ✅ 用useCallback缓存函数
const Parent = () => {
const handleClick = useCallback(() => {
console.log("点击了我");
}, []); // 依赖项为空,函数只创建一次
return <Child onClick={handleClick} />;
};
useMemo
类似,但用于缓存计算结果:
jsx
const expensiveValue = useMemo(() => {
return computeExpensiveValue(a, b); // 只有a或b变化时才重新计算
}, [a, b]);
✅ 技巧3:列表渲染用key,但别用index!
错误示范:
jsx
{items.map((item, index) => (
<Item key={index} {...item} /> // ❌ 用index当key,性能杀手!
))}
正确做法:
jsx
{items.map((item) => (
<Item key={item.id} {...item} /> // ✅ 用唯一ID
))}
为什么?
index
作为key 会导致React在列表变动时错误复用DOM,可能引发渲染错乱 或性能下降。- 唯一ID (如
item.id
)能帮助React精准识别列表项,减少不必要的DOM操作。
✅ 技巧4:懒加载(React.lazy + Suspense)
如果你的应用很大,首屏加载慢,可以用代码分割(Code Splitting) :
jsx
const HeavyComponent = React.lazy(() => import("./HeavyComponent"));
function App() {
return (
<Suspense fallback={<div>加载中...</div>}>
<HeavyComponent />
</Suspense>
);
}
这样,HeavyComponent
只会在需要时加载,减少首屏负担。
✅ 技巧5:避免在render里做高开销计算
反例:
jsx
function Component({ list }) {
const filteredList = list.filter(item => item.isActive); // 每次渲染都计算
return <List items={filteredList} />;
}
优化后:
jsx
function Component({ list }) {
const filteredList = useMemo(() => {
return list.filter(item => item.isActive); // 只有list变化时才计算
}, [list]);
return <List items={filteredList} />;
}
3. 进阶优化:useReducer替代useState
如果你的组件有复杂的状态逻辑 ,useState
可能会导致大量重新渲染。这时可以试试useReducer
:
jsx
const initialState = { count: 0 };
function reducer(state, action) {
switch (action.type) {
case "increment":
return { count: state.count + 1 };
default:
return state;
}
}
function Counter() {
const [state, dispatch] = useReducer(reducer, initialState);
return (
<button onClick={() => dispatch({ type: "increment" })}>
点了{state.count}次
</button>
);
}
优点:
- 更适合管理复杂状态
- 减少不必要的
setState
调用
4. 终极武器:React Profiler
如果你不确定哪里卡顿,可以用React DevTools的Profiler:
- 打开Chrome DevTools → React → Profiler
- 点击录制,操作页面
- 分析哪些组件渲染次数过多
总结
React性能优化不是一蹴而就的,但掌握这些技巧能大幅提升应用流畅度:
React.memo
/PureComponent
→ 避免不必要的渲染useCallback
&useMemo
→ 缓存函数和计算值- 列表key用唯一ID → 别用
index
! - 懒加载(React.lazy) → 减少首屏加载时间
useReducer
管理复杂状态 → 减少setState
触发- React Profiler定位瓶颈 → 精准优化
⭐ 写在最后
请大家不吝赐教,在下方评论或者私信我,十分感谢🙏🙏🙏.
✅ 认为我某个部分的设计过于繁琐,有更加简单或者更高逼格的封装方式
✅ 认为我部分代码过于老旧,可以提供新的API或最新语法
✅ 对于文章中部分内容不理解
✅ 解答我文章中一些疑问
✅ 认为某些交互,功能需要优化,发现BUG
✅ 想要添加新功能,对于整体的设计,外观有更好的建议
✅ 一起探讨技术加qq交流群:906392632
最后感谢各位的耐心观看,既然都到这了,点个 👍赞再走吧!