某些组件每次父组件更新就“无辜”重渲染,明明数据没变,却还是重新走一遍逻辑。React.memo 就是用来阻止这种“白干活”的神器。今天我们从监控重渲染开始,到 React.memouseMemoProfiler 一起上阵,教你把性能问题揪出来并解决。

🎯 为什么组件会重新渲染?

  • 父组件重新渲染 → 子组件默认也重渲染。
  • props 引用发生变化(即使内容一样)。
  • context/state 改变,影响所有订阅它的组件。

我们的目标是让“没有变化的数据”跳过渲染,让“确实变化的数据”尽早渲染。

🧪 React.memo 最基础用法

const UserCard = React.memo(function UserCard({ user }) {
  console.log('render', user.name);
  return <div>{user.name}</div>;
});
  • React 会对比 user prop 的引用是否变化。
  • 如果引用没变,就直接复用上一次的结果。

🧠 自定义对比函数

有时 user 是一个新对象,但内容没变。可以传入 areEqual

const UserCard = React.memo(
  function UserCard({ user }) { /* ... */ },
  (prevProps, nextProps) => prevProps.user.id === nextProps.user.id
);
  • 注意自定义函数要非常轻量,不然得不偿失。

🔍 监控渲染:为什么总是多渲染?

1. 为什么要监控?

  • 没有数据支持的优化只是“心理安慰”。
  • Profiler 能告诉你:组件渲染了几次、耗时多少。

2. 如何使用 React Profiler

  1. 打开 React DevTools → Profiler。
  2. 点击“开始录制”。
  3. 触发页面操作。
  4. 查看每个组件的渲染情况。

3. 快速定位“罪魁祸首”

  • 寻找渲染次数过多的组件。
  • 观察 props 值是否频繁变动(例如函数、数组)。

🧩 综合案例:带筛选的用户列表

const UserList = React.memo(({ users, onSelect }) => {
  console.log('UserList render');
  return (
    <ul>
      {users.map((user) => (
        <li key={user.id} onClick={() => onSelect(user)}>
          {user.name}
        </li>
      ))}
    </ul>
  );
});

function App() {
  const [keyword, setKeyword] = useState('');
  const [selected, setSelected] = useState(null);

  const filteredUsers = useMemo(() => {
    return data.filter((user) => user.name.includes(keyword));
  }, [keyword]);

  const handleSelect = useCallback((user) => setSelected(user), []);

  return (
    <div>
      <input value={keyword} onChange={(e) => setKeyword(e.target.value)} />
      <UserList users={filteredUsers} onSelect={handleSelect} />
      {selected && <UserDetail user={selected} />}
    </div>
  );
}
  • UserListReact.memo
  • usersonSelectuseMemo/useCallback 保持引用稳定。
  • Profiler 截图:输入框改变时,UserList 只在 keyword 变化时重渲染一次。

🧯 注意事项

问题 解决方案
组件内部仍依赖 context 即使用了 memo 也会跟着 context 更新
memo 组件传递匿名函数 useCallback 包裹
数据浅比较不够 areEqual 或在父组件处理数据
过度 memo 化 小型纯展示组件不用 memo,成本大于收益

🔁 配合 Suspense 和 Concurrent Mode

  • 在 React 18 并发模式下,React.memo 仍然有用。
  • Suspense 的 fallback 渲染也会触发子组件渲染,要关注渲染次数。

✅ 博客标题灵感

  • 《React.memo 真能优化性能吗?我用 Profiler 测了 3 个例子》
  • 《告别无意义重渲染:React.memo + useCallback 实战指南》
  • 《React 重渲染排查:一步一步用 Profiler 切出性能肥肉》

🏁 小练习

  1. 实际项目里找一个重复渲染的列表,用 Profiler 记录优化前后数据。
  2. 写一个 useWhyDidYouUpdate Hook,调试时输出 props 变化详情。
  3. 对比 React.memomemo + areEqual 的性能差异,写成图表。
Logo

Agent 垂直技术社区,欢迎活跃、内容共建。

更多推荐