React 性能优化三剑客:useCallback、useMemo、React.memo,别再滥用了 前言React 组件有一个特性父组件重新渲染默认所有子组件也会跟着重新渲染哪怕子组件 props 完全没变。当页面组件层级很深、渲染量大的时候无效重渲染就会带来卡顿。于是就有了这三个记忆化 API用来减少不必要渲染与重复计算。一.React.memo缓存组件高阶组件React.memo是高阶组件包裹函数组件对组件的props浅层对比。props 没有变化直接复用上次渲染结果跳过组件重渲染props 发生变化正常重新渲染只对比 props组件内部statecontext改变组件依旧会重新渲染。基本用法import { memo } from react // 用memo包裹子组件 const Child memo((props: { count: number }) { console.log(子组件执行渲染) return div子组件count{props.count}/div })致命坑点只做浅比较如果 props 传对象、数组、函数父组件每次渲染都会生成全新引用React.memo直接失效。核心作用React.memo 的核心作用就是保护子组件避免父组件更新时子组件无意义重渲染。具体来说当父组件因为自身状态state或上下文context变化而重新渲染时如果传递给子组件的 props 没有发生变化React.memo 会阻止子组件执行渲染逻辑直接复用上一次的渲染结果。这能有效减少组件树的渲染开销提升应用性能尤其是在渲染成本较高的复杂组件或列表项中。二.useCallback缓存函数引用useCallback专门缓存函数。依赖数组不变时返回同一个函数引用不会每次渲染新建函数。基本用法const handleClick useCallback(() { // 函数逻辑 }, [依赖项])回调函数你想要缓存的业务函数依赖数组函数内部用到的外部变量必须全部声明依赖不变返回同一个函数引用依赖发 生变化才会生成新函数关键要点1.它只缓存函数引用不会自动阻止子组件渲染想要保护子组件必须搭配 React.memo2.依赖数组遵循 Hook 规则不能漏写内部引用的变量否则会出现闭包陷阱3.如果依赖数组传空 [ ]代表只在组件首次渲染创建一次函数之后永远复用同一个引用。最简示例import { useCallback, useState } from react function Demo() { const [count, setCount] useState(0) // 缓存函数依赖 [count] const handleAdd useCallback(() { setCount(count 1) }, [count]) return button onClick{handleAdd}1 {count}/button }适用场景1.函数作为 props 传给被React.memo包裹的子组件稳定函数引用防止子组件无效渲染2.函数作为useEffect、其他 hook 的依赖项。三.useMemo缓存计算结果与引用useMemo缓存值执行回调把返回结果缓存。依赖不变直接读取缓存不重复执行计算逻辑。基本用法const filterList useMemo(() { // 高开销计算 return list.filter(item item 10) }, [list])关键要点1.接收两个参数计算函数、依赖数组const value useMemo(() 计算逻辑, [依赖])2.依赖不变 → 直接复用上次计算结果不重新执行函数依赖变化重新计算。3. 不要用来缓存普通简单计算本身有开销只适合开销大的复杂计算。4.组件重新渲染不等于一定会执行 useMemo看依赖是否改变。5.不能写副作用不要放接口请求副作用用 useEffect。6.空依赖 []只在组件挂载时执行一次。最简示例import { useState, useMemo } from react export default function Demo() { const [count, setCount] useState(0) const [list, setList] useState([1,2,3,4,5]) // 昂贵计算数组求和 const total useMemo(() { console.log(执行求和计算) return list.reduce((sum, item) sum item, 0) }, [list]) // 只依赖list return ( div pcount{count}/p plist总和{total}/p {/* 修改count组件重渲染list没变求和不会执行 */} button onClick{() setCount(c c 1)}count1/button {/* 修改list依赖变化重新执行求和 */} button onClick{() setList(prev [...prev, 10])}push 10/button /div ) }适用场景1. 复杂、耗时的计算逻辑2. 需要稳定引用的对象 / 数组作为 props 传给 memo 子组件四.三者配合完整示例React.memo控制子组件要不要渲染useCallback稳定函数useMemo稳定对象 / 数组。三者经常搭配出现。import { useState, useCallback, useMemo, memo } from react // 子组件开启props浅比较 const Child memo(({ onClick, list }: { onClick: () void; list: number[] }) { console.log(子组件渲染) return ( div button onClick{onClick}子组件按钮/button div{JSON.stringify(list)}/div /div ) }) export default function Parent() { const [count, setCount] useState(0) // 缓存函数引用稳定 const handleChildClick useCallback(() { console.log(子组件触发) }, []) // 缓存数组引用稳定 const dataList useMemo(() { return [11, 22, 33] }, []) return ( div p父组件count{count}/p button onClick{() setCount(c c 1)}父组件1/button {/* 父组件state更新handleChildClick、dataList引用不变子组件不会重渲染 */} Child onClick{handleChildClick} list{dataList} / /div ) }五.三者具体区别对比核心一句话区分1.React.memo组件层面缓存组件控制组件要不要重新渲染高阶组件2.useCallbackhook缓存函数实例返回函数3.useMemohook缓存计算结果值返回变量值API作用对象返回值目的依赖位置React.memo组件包装后的新组件props 不变跳过子组件渲染比较 props包裹子组件定义处useCallback函数缓存后的函数稳定函数引用防止 props 引用变化依赖数组组件内部useMemo计算逻辑缓存后的变量值避免重复昂贵计算稳定对象 / 数组引用依赖数组组件内部