使用 memo 和不使用 memo 的区别

110 阅读2分钟

React 的默认渲染机制

// 未使用 memo 的情况
function HelloWorld({ result }) {
  console.log("HelloWorld被渲染~")
  return <div>{result}</div>
}
  1. 默认行为

    • React 采用"自上而下"的递归渲染策略
    • 当父组件重新渲染时,React 会自动重新渲染所有子组件
    • React 不会自动比较 props 是否发生变化
    • 这种机制确保 UI 与数据的一致性,但可能导致不必要的渲染
  2. 渲染过程

    父组件状态更新
         ↓
    父组件重新渲染
         ↓
    子组件函数被调用
         ↓
    生成新的虚拟 DOM
         ↓
    进行 DOM 更新
    

memo 的工作机制

const HelloWorld = memo(function HelloWorld({ result }) {
  console.log("HelloWorld被渲染~")
  return <div>{result}</div>
})
  1. 浅比较机制

    • memo 会对组件的 props 进行浅比较(shallow compare)
    • 只有当 props 发生变化时,组件才会重新渲染
    • 浅比较意味着只比较对象的第一层属性
  2. 渲染过程

    父组件状态更新
         ↓
    父组件重新渲染
         ↓
    memo 进行 props 浅比较
         ↓
    props 未变化 → 使用之前的渲染结果
    props 已变化 → 重新渲染子组件
    

具体示例分析

const App = () => {
  const [count, setCount] = useState(0)
  const result = useMemo(() => calcNumTotal(50), [])

  return (
    <div>
      <h2>计数:{count}</h2>
      <button onClick={() => setCount(count + 1)}>+1</button>
      <HelloWorld result={result} />
    </div>
  )
}

不使用 memo 时的渲染流程:

  1. 点击按钮,count 更新
  2. App 组件重新渲染
  3. HelloWorld 组件无条件重新渲染
  4. 即使 result 没有变化,子组件仍然渲染

使用 memo 时的渲染流程:

  1. 点击按钮,count 更新
  2. App 组件重新渲染
  3. memoHelloWorld 的 props 进行浅比较
  4. 发现 result 未变化,跳过子组件渲染
  5. 直接复用上一次的渲染结果

memo 的性能考虑

  1. 优点

    • 避免不必要的渲染
    • 提高应用性能
    • 减少不必要的计算
  2. 潜在成本

    • 进行 props 比较需要额外的计算开销
    • 维护缓存结果需要额外的内存开销
  3. 使用建议

    • 组件经常接收相同 props
    • 组件渲染开销较大
    • 组件渲染结果相对稳定

总结

  1. 原理区别

    • 不使用 memo:遵循 React 的默认渲染机制,父组件更新导致子组件无条件更新
    • 使用 memo:增加了 props 比较的环节,只在必要时更新子组件
  2. 性能影响

    • 不使用 memo:可能导致不必要的渲染,但没有比较 props 的开销
    • 使用 memo:避免不必要的渲染,但有 props 比较的开销
  3. 最佳实践

    • 对于简单组件,可以不使用 memo
    • 对于复杂组件或频繁重渲染的组件,建议使用 memo
    • 配合 useMemouseCallback 使用效果更好