useMemo、useCallback、React.memo,可能真的要删了

"你写了3年的性能优化代码,可能全是无用功。"


React 官方的一记重锤

React 官方博客最新更新:

React Compiler 已正式推荐用于生产环境。

这意味着什么?

过去(手动优化) 现在(Compiler自动)
手写 useMemo 缓存计算结果 编译器自动识别并优化
手写 useCallback 缓存函数 编译器自动处理
手写 React.memo 包裹组件 编译器自动决定是否需要

一句话:你做的事情,编译器现在会帮你做了。

而且它做得比你好。

💡 三年前你学的React性能优化,正在变成"过时的最佳实践"。技术的残酷就在于:你越熟练的东西,越可能被淘汰。


为什么我们需要 useMemo?

先回顾一下历史。

React 的渲染机制是:状态变了,组件重新渲染。

问题来了:

jsx 复制代码
function ProductList({ products }) {
  // 每次渲染都会重新计算
  const sortedProducts = products.sort((a, b) => a.price - b.price);
  
  return <List items={sortedProducts} />;
}

如果 products 有1000个,每次父组件更新,这个排序都要跑一遍。

即使 products 根本没变。

于是我们学会了:

jsx 复制代码
const sortedProducts = useMemo(() => {
  return products.sort((a, b) => a.price - b.price);
}, [products]);

只有 products 变化时才重新计算。

这就是 useMemo 的价值------手动告诉React:"这里需要优化。"

💡 useMemo 本质上是你和 React 的"交流方式":你得告诉它哪里需要缓存。问题是,你经常说错。


React Compiler:让编译器来决定

React Compiler 的核心思想:

你不需要告诉我哪里需要优化。我自己会分析。

它的工作原理:

复制代码
源代码 → 静态分析 → 识别可优化点 → 自动插入缓存逻辑 → 输出优化后的代码

编译器会分析你的代码,自动识别:

  • 哪些计算结果可以缓存
  • 哪些函数引用需要稳定
  • 哪些组件可以跳过重渲染

你写普通代码,编译器输出优化代码。

来看对比:

Before(手动优化)

jsx 复制代码
function ProductPage({ productId }) {
  const [count, setCount] = useState(0);
  
  // 手动缓存
  const product = useMemo(() => {
    return fetchProduct(productId);
  }, [productId]);
  
  // 手动缓存回调
  const handleClick = useCallback(() => {
    setCount(c => c + 1);
  }, []);
  
  return (
    <div>
      <MemoizedButton onClick={handleClick} />
      <ProductDetail product={product} />
    </div>
  );
}

// 手动包裹
const MemoizedButton = React.memo(Button);

After(Compiler时代)

jsx 复制代码
function ProductPage({ productId }) {
  const [count, setCount] = useState(0);
  
  // 直接写,编译器自己优化
  const product = fetchProduct(productId);
  
  const handleClick = () => {
    setCount(c => c + 1);
  };
  
  return (
    <div>
      <Button onClick={handleClick} />
      <ProductDetail product={product} />
    </div>
  );
}

代码量减少 40%。

可读性提升 100%。

💡 最好的优化,是你感觉不到的优化。React Compiler让你回归"写代码"本身,而不是"写如何优化"。


编译器真的比人聪明吗?

不是"聪明",是"不会犯错"。

人工优化的常见问题:

问题 后果
过度优化 到处加 useMemo,反而增加内存开销
依赖数组错误 缓存了但没生效,或者不该更新的更新了
优化了不该优化的 简单计算加缓存,成本比收益高
忘记优化该优化的 真正的瓶颈没发现

编译器不会犯这些错。

它基于静态分析,精确识别需要优化的位置,不多不少。

💡 人会过度优化,也会忘记优化。编译器只做必要的优化。这就是机器的优势:不会"手抖"。


什么时候还需要手动优化?

别急着删光。有些场景编译器还搞不定:

1. 非纯函数调用

jsx 复制代码
// 编译器无法优化这种情况
const now = Date.now(); // 每次调用结果不同
const random = Math.random();

2. 外部状态依赖

jsx 复制代码
// 依赖了组件外部的可变状态
let externalCounter = 0;

function Component() {
  const value = externalCounter++; // 编译器无法追踪
}

3. 第三方库的特殊要求

某些库明确要求传入稳定引用(如 React Query 的 queryFn)。

这时候还是需要手动处理。

速查表

场景 需要手动优化?
普通计算/过滤/排序 ❌ 不需要
事件处理函数 ❌ 不需要
子组件 props ❌ 不需要
非纯函数 ✅ 可能需要
第三方库特殊要求 ✅ 根据文档

💡 80%的手动优化可以删掉。剩下20%,等你遇到真正的性能问题再加不迟。


迁移指南:怎么开始用?

Step 1: 检查环境

React Compiler 需要:

  • React 19+
  • Babel / SWC 配置

Step 2: 安装配置

bash 复制代码
npm install babel-plugin-react-compiler
js 复制代码
// babel.config.js
module.exports = {
  plugins: [
    ['babel-plugin-react-compiler', {
      // 配置项
    }]
  ]
};

Step 3: 渐进式清理

不需要一次性删光。推荐步骤:

  1. 先在新代码中不写 useMemo/useCallback
  2. 跑一段时间,观察性能
  3. 逐步清理旧代码中的手动优化
  4. 保留真正需要的边界情况

Step 4: 监控性能

删除手动优化后,用 React DevTools Profiler 检查:

  • 渲染次数是否异常增加
  • 是否有明显的性能下降

如果没有问题------恭喜,你的代码变干净了。


写给React开发者的话

技术在进化。

曾经的最佳实践:

  • 2018:Class组件 + 生命周期
  • 2020:Hooks + useMemo/useCallback
  • 2024:React Compiler + 自动优化

每一代"最佳实践"都会被下一代淘汰。

这不是坏事。

说明工具在变好,开发者的负担在减轻。

💡 真正的高手不是记住所有API,而是知道什么时候该忘掉它们。useMemo 是好东西,但好东西也有过期的时候。


今晚就试试:

把项目里的 useMemo 删几个,跑跑看。

你会发现,天没塌。

评论区告诉我:你项目里有多少个 useMemo?敢删吗?

相关推荐
滕青山3 小时前
Vue项目BMI计算器技术实现
前端·vue.js
子兮曰3 小时前
深入浏览器指纹:Canvas、WebGL、Audio是如何暴露你的身份的?
前端·浏览器·canvas
月亮补丁3 小时前
AntiGravity只能生成 1:1 图片?一招破解尺寸限制
前端
何中应3 小时前
MindMap部署
前端·node.js
NAGNIP3 小时前
程序员效率翻倍的快捷键大全!
前端·后端·程序员
一个网络学徒3 小时前
python5
java·服务器·前端
tiantian_cool3 小时前
Claude Opus 4.6 模型新特性(2026年2月5日发布)
前端
0思必得03 小时前
[Web自动化] Selenium获取元素的子元素
前端·爬虫·selenium·自动化·web自动化
用户5757303346243 小时前
🌟 从一行 HTML 到屏幕像素:浏览器是如何“画”出网页的?
前端