memo 与属性稳定性
memo 与属性稳定性 · 阅读后完成下方实验与检查题,再用「标记完成」记录进度。
学习目标
读完你能解释「父组件一渲染,列表所有子项跟着重渲染」的原因,会用 React.memo 把重渲染挡在组件边界之外,并用 useCallback / useMemo 稳定传下去的属性引用;同时掌握验证习惯:优化后渲染次数必须下降,而交互结果必须和优化前完全一致。
问题场景
课程收藏页:顶部一个搜索框,下面 60 张课程卡片。在搜索框里每敲一个字,React DevTools 的高亮闪成一片——60 张卡片全部重渲染,尽管它们的 props 一个都没变。原因在于 React 的默认策略:父组件渲染时,所有子组件默认跟着渲染,不管 props 变没变。列表越长,这个「陪着渲染」的浪费越明显。
核心解释
memo 是组件级的缓存:组件包上 memo 后,React 在父组件渲染时会先对 props 做一次浅比较,全部相同就跳过本次渲染,直接复用上一次的渲染结果:
const CourseCard = memo(function CourseCard({ course, onFav }) {
console.log("render:", course.title); // 观察渲染次数
return (
<div>
{course.title} <button onClick={() => onFav(course.id)}>收藏</button>
</div>
);
});
但浅比较救不了「每次渲染都新建」的值:内联箭头函数、内联对象字面量、filter 出来的新数组,每次渲染都是新引用,浅比较必然不相等,memo 等于白包——这是最常见的「假优化」。所以要配合引用稳定化:
const hot = useMemo(() => courses.filter((c) => c.hot), [courses]); // 稳定数据引用
const handleFav = useCallback((id) => toggleFav(id), [toggleFav]); // 稳定函数引用
useMemo 稳定数据引用,useCallback 稳定函数引用,让到达 memo 组件的 props 真正「没变」。这正是上一课说的维护成本现场:依赖数组写漏一项,稳定性破功,memo 失效——所以每一步都要验证,不能包完就信。
验证手段有两个:组件里的渲染计数(console.log 或页面上的计数徽标),以及 React DevTools Profiler 的火焰图。优化后的回归检查只需两条:渲染次数降下来了;点收藏、输入关键词等交互的结果和优化前一致——memo 只改变「渲染不渲染」,不改变「结果对不对」。
示例拆解
实验区 60 张课程卡片 + 搜索框 + 三种模式:
- 每张卡片上有小字显示自己累计渲染的次数(仅测量用,正式代码别这么写);
- 「不加 memo」:输入一个字,60 张卡片计数全部 +1;
- 「memo + 内联函数」:卡片包了 memo,但 onFav 还是每次渲染新建的箭头函数——照样全部 +1,这就是最容易踩的假优化;
- 「memo + useCallback」:函数引用稳定了,输入时所有卡片计数都不动;
- 验证:三种模式各输入几轮关键词,对比计数增长;再点几次收藏,确认收藏功能与优化前行为一致。
动手任务
打开本课实验区:
- 把 useCallback 的依赖数组从
[]改成[favs],再输入关键词:观察为什么所有卡片又重渲染了,说出依赖数组里「多余的项」如何破坏 memo。 - 给搜索框加一个「清空」按钮(只 setKeyword("")),确认 useCallback 模式下点击它所有卡片都不重渲染。
- 把 favs 传进卡片并显示收藏标记 ★,确认点击收藏时只有目标卡片重渲染,其余卡片计数不动。
常见错误
- 包了 memo 却传内联函数/对象:浅比较每次不等,优化无效,还多了比较开销。
- 为了 memo 把组件拆得过碎:边界太多,维护引用稳定性的代码比省下的渲染还贵;先测量(R34)再拆。
- 优化后不做行为回归:依赖数组写错时可能出现「props 看起来没变而实际该变」,交互走一遍才能确认。
- 以为 memo 能挡住 context 变化:memo 组件消费的 context 值变化时照样重渲染,memo 只比较 props。
检查与复盘
自查清单:
- 能说清 memo 的浅比较语义和「内联函数/内联对象/新数组」三类假优化现场
- 传给 memo 组件的函数和数据,知道分别用 useCallback / useMemo 稳定
- 每次优化都验证过两件事:渲染次数下降,交互结果不变
参考资料
- React 官方文档:memo —— memo 的语义、跳过渲染的条件与限制
- React 官方文档:useCallback —— 稳定函数引用的 API 约定
- React 官方文档:渲染与提交 —— 理解「渲染」到底发生了什么,测量才有意义
读完这一课了?
标记完成后,工作台会安排接下来的学习。