别再乱用 useMemo 了!React 性能优化三大缓存 Hook 对比 + 与 Vue computed 的本质区别

86 阅读3分钟

最近在 review 代码时,发现不少同学对 useMemo 的使用存在误解:

  • 有的地方该用不用,导致性能浪费;
  • 有的地方滥用,反而增加开销;
  • 更有人把它和 useRefuseCallback 混为一谈……

今天,我就用一个简单例子 + 三大对比 + 一个灵魂拷问,带你彻底搞懂 useMemo 的正确打开方式!

一、先看一个经典场景:为什么需要 useMemo?

假设我们有一个计数器,还有一个“切换主题”按钮:

carbon (3).png

问题来了:当我点击“切换主题”时,squared 会重新计算吗?

✅ 答案是:会!
因为每次状态更新都会触发组件重新执行,count * count 就会被重复计算。

虽然乘法很快,但如果这是个复杂操作(如排序、格式化、深度克隆),就会造成不必要的性能损耗


二、useMemo:只在依赖变化时才计算

解决方案就是 —— useMemo

carbon (4).png

现在:

  • 点击  +1 → 重新计算 ✅
  • 点击 切换主题 → 不计算 ❌(控制台无日志)

✨ useMemo 的作用:缓存计算结果,依赖不变就不重算。


三、三大缓存 Hook 对比:别再混用了!

很多同学分不清 useMemouseCallbackuseRef,其实它们目标完全不同

Hook缓存什么?典型用途返回值
useRef一个可变容器(.current存 DOM 引用、定时器 ID、跨渲染变量{ current: T }
useCallback函数防止函数重建导致子组件重渲染Function
useMemo任意值(对象/数组/计算结果)避免重复执行昂贵计算T

💡 一句话区分:

  • 想缓存一个值(不是函数)→ useMemo
  • 想缓存一个函数 → useCallback
  • 想存一个不触发渲染的变量 → useRef

⚠️ 常见误区:
“我用 useRef 存计算结果不就行了?”
→ 技术上可行,但你得手动判断依赖是否变化,极易出错且不可维护


四、灵魂拷问:React 的 useMemo vs Vue 的 computed?

很多从 Vue 转 React 的同学会问:

useMemo 是不是就等于 Vue 的 computed?”

答案是:相似,但底层机制完全不同!

特性Vue computedReact useMemo
响应式原理自动依赖追踪(Proxy 劫持)手动声明依赖(依赖数组)
更新粒度细粒度(只更新用到的数据)组件级(整个函数重执行)
是否自动缓存✅ 是✅ 是(但需手动写)
心智模型“数据变了,视图自动跟上”“状态变了,我重新描述 UI”

🌰 举个例子:
在 Vue 中,你写 computed: { fullName() { return this.firstName + this.lastName } },框架自动知道它依赖 firstName 和 lastName
而在 React 中,你必须显式写出 [firstName, lastName],否则会用旧值!

✅ 所以:

  • Vue 的 computed 更“智能”
  • React 的 useMemo 更“显式可控”

五、使用 useMemo 的 3 个最佳实践

  1. 不要过早优化
    简单计算(如 a + b)不需要 useMemo,反而增加内存开销。

  2. 依赖数组必须写全
    漏掉依赖会导致“stale closure”(闭包陷阱),用 ESLint 插件 exhaustive-deps 自动检查。

  3. 主要用于以下场景

    • 昂贵的计算(排序、过滤、格式化)
    • 创建新对象/数组(避免子组件因引用变化重渲染)
    • 传递 props 给 React.memo 包裹的子组件

六、总结

  • useMemo 不是万能药,但它是性能优化的关键工具
  • 它和 useCallbackuseRef 各司其职,不要混用
  • 与 Vue 的 computed 相比,React 更强调显式依赖声明,这是函数式思维的核心。

🔑 记住
“useMemo 缓存的是值,useCallback 缓存的是函数,useRef 存的是盒子。”