详解:useMemo 和useCallback

一、核心区别

  • useMemo:缓存 计算结果(值)
  • useCallback:缓存 函数本身

它们的工作原理完全一样依赖不变 → 直接用缓存;依赖变了 → 重新生成

二、useMemo 详细讲解

1.作用

缓存一个值(对象、函数、计算结果),避免组件重新渲染时重复计算。

2.语法

复制代码
const 缓存值 = useMemo(计算函数, 依赖数组);

3.工作流程

组件第一次渲染:执行计算结果 =》存结果

组件后续渲染:如果依赖数组里的值没变 =》直接用缓存,不执行计算函数。如果依赖数组里任意一个值变了 =》 重新执行计算函数并更新缓存。

4.什么时候必须要用useMemo?

第一种情况:耗时计算(最常见用法)

复制代码
// 大数据过滤、排序、复杂计算
const expensiveResult = useMemo(() => {
  return hugeList.filter(...).map(...).sort(...)
}, [hugeList]);

不用 useMemo → 组件一渲染就重新算 → 会导致页面卡顿

第二种情况:缓存引用类型(对象/数组)

复制代码
// 每次渲染都会生成新对象
const style = { color: 'red' };

// 用 useMemo 缓存,引用永远不变
const style = useMemo(() => ({ color: 'red' }), []);

为什么要缓存引用?

因为对象 / 数组是引用类型,每次渲染都会变新地址,会导致子组件不必要重渲染。

怎么理解会导致子组件不必要重渲染?

父组件每更新一次,函数重新执行一遍。只要你在组件里直接写对象/数组:

复制代码
function Parent(){
  // 每次渲染 = 新建一个全新对象,新内存地址
  const obj = { id:1, name:"张三" } 

  return <Child data={obj}/>
}

每一次重渲染 → 生成全新引用地址

子组件用了React.memo 做浅层对比优化:

  • 子组件接收data props
  • 父渲染 → obj 换新地址
  • memo 对比发现props 地址变了
  • 直接判定 props 变了 → 强制重渲染子组件
  • 明明内容没变,纯浪费性能

什么时候不要用useMemo?

  • 固定加减、字符串拼接
  • 固定不变的值。缓存本身也有开销,过度用反而会变慢。

三、useCallback 详细讲解

1.作用

缓存函数本身,让函数在依赖数组不变时,永远时同一个函数引用。

2.语法

复制代码
const 缓存函数 = useCallback(函数体, 依赖数组);

3.工作流程

组件首次渲染:执行useCallback,创建函数 + 固定内存地址,返回缓存好的函数。

组件再次重渲染,先对比依赖数组:如果依赖没变 =》直接返回旧函数、旧地址,不新建函数。如果依赖变了 =》重新创建函数,更新引用地址。

再传给子组件,搭配React.memo,函数引用不变 =》props 不变 =》子组件不重复渲染。

4.为什么要用useCallback?

看这个例子你立刻懂:

复制代码
function Parent() {
  const handleClick = () => { console.log('click') };

  return <Child onClick={handleClick} />
}

问题:Parent 一渲染 → handleClick 就变成新函数 → Child 收到新 props → Child 被迫重渲染。

用 useCallback 修复:

复制代码
const handleClick = useCallback(() => {
  console.log('click')
}, []); // 依赖空数组 → 永远不变

5. useCallback 必须配合 memo 使用!

单独用 useCallback 没用!必须配合子组件的memo 才能生效:

复制代码
// 子组件用 memo 包裹:只有 props 变了才渲染
const Child = memo(({ onClick }) => { ... });

现在 Child 不会因为父组件渲染而重复渲染。

为什么useCallback 必须配合memo 使用?

React 默认规则是,父组件一更新,所有子组件无条件跟着重新渲染。不管你函数地址变没变,照样渲染。

而useCallback 只能把函数地址稳住不变。父组件管他怎么渲染,只要依赖不变,函数地址永远不变。但他自己不能阻止任何渲染。

memo 是子组件是否要渲染的判断开关。React.memo(子组件),作用:子组件自带 props 浅层对比。props 全没变 =》跳过渲染。props 变了 =》正常渲染

完整生效链路

  1. 父组件用useCallback =》函数引用不变
  2. 子组件包memo =》检测到 props 没变化
  3. 最终:子组件不重渲染

四、一个惊人的等价关系

观察下面代码:

复制代码
// useMemo → 缓存值
const value = useMemo(() => {
  return 100; // 返回值
}, []);

// useCallback → 缓存函数
const fn = useCallback(() => {
  return 100; // 函数体
}, []);

useCallback(fn, deps) 完全等于useMemo(() => fn, deps)

也就是说:useCallback 是useMemo 的语法糖,专门用来缓存函数。

五、useMemo 和React.memo

React.memo 是组件级别缓存,防止组件重复渲染。作用是给函数组件做浅层props 比对,props 没变就跳过重渲染,props 变了就正常渲染。

useMemo 是值级别缓存,缓存计算结果/引用。作用是根据依赖数组判断是否需要重新执行计算函数,避免重新执行不必要的复杂计算。

需要注意的是,如果使用useMemo 缓存函数,也需要配合React.memo 使用,优化才会生效。

六、小结

  • useMemo 缓存值,useCallback 缓存函数

  • 它们都靠依赖数组决定是否更新

  • 目的都是避免不必要的重渲染 / 重复计算

  • useCallback 必须配合memo 子组件才能真正优化

相关推荐
hexu_blog4 小时前
前端vue3后端springboot如何实现图片压缩-免费无限制压缩
前端·java压缩图片·vue压缩图片·批量压缩图片
guslegend4 小时前
第11节:前端 UI 设计与前端基础组件
前端·ui·ai编程
摇滚侠4 小时前
13 移动端 WEB 前端 WEB 开发 HTML5 + CSS3 + 移动 WEB
前端·css3·html5
就爱瞎逛4 小时前
解决Ant Design Vue 日期选择器中文不生效
前端·javascript·vue.js
JAVA面经实录9174 小时前
Java集合100道面试真题(背诵完整版)
java·python·面试
快递鸟社区4 小时前
快递鸟海运查询接口全面解析:从入门到精通,助力跨境物流可视化
java·前端·人工智能
踩着两条虫4 小时前
可视化设计器组件系统:从交互核心到 AI 智能代理的落地实践
开发语言·前端·人工智能·低代码·设计模式·架构
光影少年4 小时前
大前端框架生态
前端·javascript·flutter·react.js·前端框架·鸿蒙·angular.js
知彼解己4 小时前
前端发布流程总结(Vue + Element 项目)
前端·javascript·vue.js