当前位置: 首页 > java >正文

从 Vue 到 React:React.memo + useCallback 组合技

目录

    • 一、Vue 与 React 的组件更新机制对比
    • 二、React.memo 是什么?
    • 三、常见坑:为什么我用了 React.memo 还是会重新渲染?
    • 四、解决方案:useMemo / useCallback 缓存引用
    • 五、Vue 3 中有类似的性能控制需求吗?
    • 六、组合优化小技巧总结
    • 七、不过话又说回来

一、Vue 与 React 的组件更新机制对比

在 Vue 中,组件的更新依赖于响应式系统的依赖追踪:

<!-- Vue 模板中 -->
<Child :data="data" />
  • 父组件更新时,Vue 会判断 data 是否变更;
  • data 是响应式对象,它会做“依赖追踪”;
  • 子组件是否更新由内部响应式系统决定,开发者很少手动优化。

而在 React 中:

<Child data={data} />
  • React 默认只做浅比较
  • 只要 data 是一个新对象(哪怕值完全相同),就会触发 Child 重新渲染;
  • 所以你需要用 React.memo + useCallback + useMemo手动优化性能。

二、React.memo 是什么?

const MemoizedComponent = React.memo(Component);

React.memo 是一个高阶组件,用于缓存函数组件的渲染结果,仅在 props 变化时才重新渲染。

它做了啥?

  • 对 props 进行浅比较
  • 如果 props 没变(===),则跳过子组件渲染;

三、常见坑:为什么我用了 React.memo 还是会重新渲染?

来看一个经典例子:

const Parent = () => {const [count, setCount] = useState(0);const data = { text: "hello" }; // ⚠️ 每次渲染都是新对象!return (<><button onClick={() => setCount(c => c + 1)}>+</button><Child data={data} /></>);
};const Child = React.memo(({ data }) => {console.log("Child render");return <div>{data.text}</div>;
});

🔎 结果:

  • 每点一次按钮,Child 都会重新渲染!
  • 虽然 data 的值没有变,但对象引用变了{} 是新对象);
  • 所以 React.memo 判定 props 变了,触发更新。

四、解决方案:useMemo / useCallback 缓存引用

const data = useMemo(() => ({ text: "hello" }), []);

或者对于函数:

const handleClick = useCallback(() => {console.log("clicked");
}, []);

这就保证了 引用不变,从而让 React.memo 的比较机制生效。


完整示例:

const Parent = () => {const [count, setCount] = useState(0);const data = useMemo(() => ({ text: "hello" }), []);const handleClick = useCallback(() => {console.log("clicked");}, []);return (<><button onClick={() => setCount(c => c + 1)}>+</button><Child data={data} onClick={handleClick} /></>);
};const Child = React.memo(({ data, onClick }) => {console.log("Child render");return <button onClick={onClick}>{data.text}</button>;
});

🧠 现在:

  • 点击按钮不会触发 Child 重新渲染;
  • 因为 dataonClick 的引用没变;
  • React.memo 正常工作,组件性能得到提升。

五、Vue 3 中有类似的性能控制需求吗?

Vue 3 的响应式机制天然做了很多“追踪 + 缓存”,组件不会因为 props 引用变化而轻易重新渲染,只要你不写复杂嵌套 watch / watchEffect,基本不用显式控制更新。

但 React 是“纯函数组件 + 浅比较 + 显式控制”模式,性能优化基本靠开发者手动干预。


六、组合优化小技巧总结

目标工具组合
避免组件重复渲染React.memo
保证函数 prop 引用稳定useCallback
保证对象 prop 引用稳定useMemo
高性能组件拆分 + 精准更新控制React.memo + useCallback + useMemo

七、不过话又说回来

关于这个优化的组合手段,在实际开发中,往往不是“用不用”,而是“什么时候用、用在哪、用多少”。盲目无脑使用很容易陷入“性能优化反而拖慢开发效率”的误区。

❌ 常见错误:

const value = useMemo(() => expensiveCalculation(data), [data]);

很多人会这么写,但:

  • 如果 expensiveCalculation() 实际上并不耗时;
  • 或者 data 频繁变化,memo 无意义;
  • 那么你加了 useMemo 不但没有优化,还增加了复杂度。

所以重点是:只有在“真的影响性能”时才用。


那【何时该用】呢?

场景是否建议使用 Memo 类 Hook
父组件频繁更新,子组件 props 不变✅ 使用 React.memo + useCallback
传递对象或函数给子组件✅ 保持引用稳定,避免不必要更新
有昂贵计算(排序、大数据处理等)✅ 使用 useMemo 缓存计算结果
props 很简单,子组件渲染开销很小(比如一个 <span>❌ 不需要,优化反而复杂
自己写了一堆 memo,但还是更新很慢❌ 可能是 Context/状态设计问题
拥有大量动态子组件(如大表格、虚拟滚动列表)✅ 搭配 memouseMemo、分块渲染等
使用 Context 时⚠️ memo 可能无效,需额外处理

下面看看两类重点问题

  1. Context 泄漏导致 memo 失效
<MyContext.Provider value={contextValue}><MemoizedChild />
</MyContext.Provider>

每次 context value 变,整个 Provider 下所有组件都会重新渲染,哪怕用了 React.memo

解决方式:

  • 避免 context value 是新对象(useMemo 包装);
  • 或者将 context 拆分成多个 Provider;
  • 或使用第三方库如 zustand 做 context 分片;

  1. 大量嵌套组件的 props 传递链

假如你有一个页面,父组件的数据变化会层层传到第 6 层子组件,那你会发现:

  • 就算第 6 层用 React.memo,如果 props 是对象或函数,还是会触发更新;
  • useMemo / useCallback 会让代码变得繁琐难读。

解决方式:

  • 使用全局状态管理(如 zustand)代替 props;
  • 通过组合组件逻辑 & 提前拆分优化更新粒度;
  • 或者用 memo + useContextSelector(experimental)做局部响应式。

Vue 的响应式机制帮我们自动完成“依赖追踪 + 缓存更新”,你不用担心引用变化;
但在 React 中,这些都需要你自己判断并手动处理。

所以:

  • 不要滥用 memo 系列 Hook,性能优化是“按需用药”;
  • 越复杂的页面越要拆组件,避免“父更新拖着全家跑”;
  • 适度引入状态管理工具,别让 props 变成“传话筒”。
http://www.xdnf.cn/news/1377.html

相关文章:

  • PCB规则
  • 【android bluetooth 协议分析 11】【AVDTP详解 2】【avdtp 初始化阶段主要回调关系梳理】
  • 基于FPGA 和DSP 的高性能6U VPX 采集处理板
  • 深入解析C++ STL Queue:先进先出的数据结构
  • Android Gradle Plugin (AGP) 和 Gradle 的關係
  • 【Qwen2.5-VL 踩坑记录】本地 + 海外账号和国内账号的 API 调用区别(阿里云百炼平台)
  • 学习记录:DAY16
  • 2.RabbitMQ - 入门
  • 从入门到精通:CMakeLists.txt 完全指南
  • AI语音助手自定义角色百度大模型 【全新AI开发套件掌上AI+4w字教程+零基础上手】
  • 永磁同步电机控制算法-反馈线性化控制
  • 官方不存在tomcat10-maven-plugin插件
  • 【模板匹配】图像处理(OpenCV)-part10
  • 【金仓数据库征文】从Oracle到KingbaseES的语法兼容与迁移
  • 常用第三方库精讲:cached_network_image图片加载优化
  • Chrome/Edge浏览器使用多屏完美解决方案,http部署使用https部署的功能
  • 互联网金融岗位简历模板
  • 3.第三章:数据治理的战略价值
  • 【人工智能】Ollama 负载均衡革命:多用户大模型服务的高效调度与优化
  • Vue3父子组件数据同步方法
  • gbase8s存储学习一 rootdbs存储结构以及寻址分析
  • 08-IDEA企业开发工具-集成AI插件通义灵码
  • Java—— 正则表达式 练习
  • 代理模式:控制对象访问的中间层设计
  • C#学习1_认识项目/程序结构
  • 【无标题】spark安装部署
  • TCP 协议:原理、机制与应用
  • cursor改Goland操作习惯
  • 密码学(1)LWE,RLWE,MLWE的区别和联系
  • 校园外卖服务系统的设计与实现(代码+数据库+LW)