⚡ 编程实验室🏗️ HTML🎨 CSS⚡ JavaScript🐍 Python🗄️ SQL☕ Java⚛️ React💚 Vue🟢 Node.js⚙️ C语言🐘 PHP🐹 Go🔷 TypeScript🐬 MySQL🔧 C++🎯 C#🦀 Rust🅱️ Bootstrap💡 jQuery🎸 Django🍃 MongoDB👗 Sass🎪 Kotlin📊 R语言📋 XML📊 Excel🐘 PostgreSQL🐳 Docker🅰️ Angular🎮 游戏🏠 网站首页

React 性能优化:memo、useMemo、useCallback 实战

掌握 React 三大性能优化 API,让你的应用告别不必要的重渲染。 · 难度:进阶 · +20XP

你的 React 应用为什么慢?

React 的默认行为是:父组件更新,所有子组件都跟着重新渲染。听起来很浪费?是的,但在大多数情况下,这种"浪费"其实影响不大——现代浏览器渲染 100 个简单组件可能只需要几毫秒。

但当你的应用有几百个列表项、复杂图表、实时数据流时,不必要的重渲染就会让页面明显卡顿。这时候你需要 React 的性能优化三件套。

React.memo:阻止不必要的子组件渲染

React.memo 是一个高阶组件,它告诉 React:"如果 props 没变,就别重新渲染这个组件"。

// 普通组件:父组件更新 → 子组件一定重新渲染
function TodoItem({ text, done }) {
  console.log('TodoItem 渲染了:', text);
  return <li style={{ color: done ? 'green' : 'black' }}>{text}</li>;
}

// 用 memo 包裹:props 没变 → 跳过渲染 const TodoItem = React.memo(function TodoItem({ text, done }) { console.log('TodoItem 渲染了:', text); return <li style={{ color: done ? 'green' : 'black' }}>{text}</li>; });

当你勾选一个待办事项时,只有那个被勾选的 TodoItem 重新渲染,其他的全部跳过。

useMemo:缓存计算结果

假设你有一个计算量很大的操作——比如对 10000 条数据进行排序和过滤:

function ProductList({ products, category }) {
  // ❌ 每次渲染都重新计算,即使 products 和 category 没变
  const filtered = products
    .filter(p => p.category === category)
    .sort((a, b) => b.sales - a.sales);

// ✅ 只在依赖变化时重新计算 const filtered = useMemo(() => { return products .filter(p => p.category === category) .sort((a, b) => b.sales - a.sales); }, [products, category]);

return filtered.map(p => <ProductCard key={p.id} product={p} />); }

Hook缓存什么何时用
React.memo整个组件子组件 props 不常变、渲染成本高
useMemo计算结果(值)计算量大、依赖稳定的操作
useCallback函数引用传给 memo 子组件的回调函数

useCallback:稳定的函数引用

这是最容易用错的一个。看这个场景:

function Parent() {
  const [count, setCount] = useState(0);

// ❌ 每次渲染都创建新的函数引用 const handleClick = () => setCount(c => c + 1);

// ✅ 函数引用保持稳定 const handleClick = useCallback(() => { setCount(c => c + 1); }, []); // 依赖为空,函数永远不会变

return <Child onClick={handleClick} />; }

const Child = React.memo(function Child({ onClick }) { console.log('Child 渲染'); return <button onClick={onClick}>+1</button>; });

不用 useCallback 的话:每次 count 变化 → Parent 重渲染 → handleClick 是新函数 → memo 的 Child 发现 props 变了 → 也重渲染。memo 白用了。

用了 useCallback:handleClick 引用不变 → Child 的 props 没变 → memo 生效 → 跳过渲染。

什么时候不应该用这些优化?

过早优化是万恶之源。以下情况不需要加 memo/useMemo/useCallback:

实战:优化一个待办列表

// 场景:100 个待办事项,可以勾选完成、可以添加新项
function TodoApp() {
  const [todos, setTodos] = useState([...100个待办...]);
  const [newText, setNewText] = useState('');

// ✅ addTodo 引用稳定,不会导致 TodoList 重渲染 const addTodo = useCallback((text) => { setTodos(prev => [...prev, { id: Date.now(), text, done: false }]); }, []);

// ✅ toggleTodo 引用稳定 const toggleTodo = useCallback((id) => { setTodos(prev => prev.map(t => t.id === id ? { ...t, done: !t.done } : t )); }, []);

return ( <div> <AddTodo onAdd={addTodo} /> <TodoList todos={todos} onToggle={toggleTodo} /> </div> ); }

// ✅ 用 memo 包裹,只在 todos 变化时渲染 const TodoList = React.memo(function TodoList({ todos, onToggle }) { return todos.map(todo => <TodoItem key={todo.id} todo={todo} onToggle={onToggle} /> ); });

动手试试

  1. 基础练习:创建一个带输入框和列表的组件,用 React DevTools Profiler 查看每次输入时的渲染次数,然后加上 memo/useCallback 对比
  2. 进阶应用:做一个 1000 行的虚拟列表,用 useMemo 缓存可见行,用 React.memo 优化每一行
  3. 项目实战:找到你项目中最"重"的页面(渲染次数最多),加 memo/useMemo/useCallback 优化,用 Profiler 量化优化效果

接下来学什么?

掌握了性能优化,下一课学习 React 自定义 Hook 实战——把重复的逻辑抽成 Hook,让代码更干净、更可复用。

📖 译自 freeCodeCamp React Performance Optimization (CC BY 4.0),有改编和扩充。

🚀 升级VIP
解锁全部课程+AI助手

🏆 学习排行

加载中...

📊 统计

📖 85 篇
0 完成
🔥 0