真实面经题目 · 原创解析
memo, useMemo, useCallback 分别是干什么的?
这题考察 React 记忆化优化边界。回答要区分组件跳过渲染、值缓存和函数引用稳定,并说明命中条件和成本。
真实面经题目 · 原创解析
这题考察 React 记忆化优化边界。回答要区分组件跳过渲染、值缓存和函数引用稳定,并说明命中条件和成本。
React.memo 是包在组件外层的高阶组件,默认对 props 做浅比较,props 没变时跳过子组件重新渲染。useMemo 是在组件内部缓存一个计算结果,依赖不变时复用上次返回值,适合昂贵计算或需要稳定对象引用的场景。useCallback 缓存的是函数引用,本质可以理解为返回函数的 useMemo,常用于把回调传给 memo 子组件或作为 effect 依赖。三者都不是免费优化:要维护依赖、做比较和占用内存,只有当跳过渲染或计算的收益大于这些成本时才值得用。
memo 包住函数组件后,父组件渲染时 React 会比较新旧 props。默认是浅比较,引用类型只比较引用是否相同。命中时跳过该子组件本次渲染。
useMemo(() => compute(), deps) 会在依赖不变时复用上次计算结果。它适合昂贵计算、筛选排序大数组、稳定对象引用和避免下游 memo 组件因新对象失效。
useCallback(fn, deps) 返回稳定的函数引用,依赖变化时才更新。它常用于把回调传给 memo 子组件、传给自定义 Hook 或避免 effect 因函数引用变化重复执行。
如果子组件没有 memo,父组件每次渲染时稳定 callback 未必带来收益。如果 memo 子组件接收的对象或函数每次都是新引用,memo 也很难命中。优化要看整条 props 链。
浅比较有成本,useMemo/useCallback 有依赖维护成本,也可能因为漏依赖产生闭包旧值。过度使用会让代码更难读,却未必减少实际渲染。
先用 React DevTools Profiler 找出频繁渲染且代价高的组件,再用 memo、useMemo、useCallback 局部优化。不要把它们当成默认模板。
useCallback(fn, deps) 可以理解为 useMemo(() => fn, deps),只是语义上专门用于缓存函数引用。
默认浅比较。基础类型比值,引用类型比引用地址。可以传自定义比较函数,但比较逻辑太重可能比重新渲染还贵。
计算很轻、依赖经常变化、结果不传给 memo 子组件、或者只是为了包一层普通对象时,收益通常不明显,还会增加复杂度。
可能读到旧 props 或旧 state,表现为回调和计算结果滞后。依赖数组不是性能开关,而是声明闭包使用了哪些外部值。