前端进阶之旅前端进阶之旅
  • 基础篇HTML/CSS/JS 打底
  • 进阶篇原理与工程化
  • 高频篇面试最常问的那批
  • 精选篇按模块收敛的总结
  • 手写篇常考代码手写实现
  • 面经篇真实面试问题复盘
  • AI 篇NEWAI 时代的前端考点
  • 历年面经NEW按年份追踪真实考点
  • 每日一题每天一道,攒手感
  • 专项自测100 题快速查漏
  • 小程序题库小程序专项刷题
  • 算法题库NEW在线编码即时判题
  • 知识卡片NEW碎片时间过考点
  • 面试题大全常见问题解析
  • AI 答疑NEW随时提问,即时解析
  • AI 模拟面试NEW模拟真实面试 + 报告
  • AI 定制路线NEW按你的简历现排
  • AI 知识地图NEW串起全站知识点
  • 原理篇React / Vue 源码拆解
  • HTTP从报文一路讲到 HTTPS
  • 浏览器渲染、事件循环、进程
  • 计算机基础Linux、网络、操作系统
  • 设计模式23 种模式怎么用
  • Node学习指南从环境搭建到服务端
  • NPM工作流script、依赖与发布
  • Docker容器化部署上手
  • Canvas图形与动画实战
  • 前端系统进阶学习大型项目工程化
  • 前端综合文章长期沉淀的实践文
  • 思维导图知识点全景图
  • 学习路线按图索骥不跑偏
  • AI 热点NEWAI 每日动态
  • 公众号动态公众号历史文章
  • 博客动态站长的技术博客
  • 开发者导航常用工具与文档站
  • 基础篇HTML/CSS/JS 打底
  • 进阶篇原理与工程化
  • 高频篇面试最常问的那批
  • 精选篇按模块收敛的总结
  • 手写篇常考代码手写实现
  • 面经篇真实面试问题复盘
  • AI 篇NEWAI 时代的前端考点
  • 历年面经NEW按年份追踪真实考点
  • 每日一题每天一道,攒手感
  • 专项自测100 题快速查漏
  • 小程序题库小程序专项刷题
  • 算法题库NEW在线编码即时判题
  • 知识卡片NEW碎片时间过考点
  • 面试题大全常见问题解析
  • AI 答疑NEW随时提问,即时解析
  • AI 模拟面试NEW模拟真实面试 + 报告
  • AI 定制路线NEW按你的简历现排
  • AI 知识地图NEW串起全站知识点
  • 原理篇React / Vue 源码拆解
  • HTTP从报文一路讲到 HTTPS
  • 浏览器渲染、事件循环、进程
  • 计算机基础Linux、网络、操作系统
  • 设计模式23 种模式怎么用
  • Node学习指南从环境搭建到服务端
  • NPM工作流script、依赖与发布
  • Docker容器化部署上手
  • Canvas图形与动画实战
  • 前端系统进阶学习大型项目工程化
  • 前端综合文章长期沉淀的实践文
  • 思维导图知识点全景图
  • 学习路线按图索骥不跑偏
  • AI 热点NEWAI 每日动态
  • 公众号动态公众号历史文章
  • 博客动态站长的技术博客
  • 开发者导航常用工具与文档站
首页程序员面试题库React.memo arePropsEqual 自定义比较
ReReact性能与并发

React.memo 的 arePropsEqual 如何编写,为什么不能忽略函数 prop?

比较器返回 true,意味着旧结果连同旧事件行为都可以继续使用,而不只是屏幕上的文字暂时相同。

前端进阶之旅 · 一题精讲更新于 2026.09.06
React#性能与并发#React 性能#性能优化#并发编程
先看核心答案读代码示例
理解线索

自定义比较器承担的三项责任

  1. 覆盖范围组件读到的显示字段、交互字段与回调都不能遗漏
  2. 数据前提旧输入保持不可变,才有可供比较的历史快照
  3. 成本边界比较数量与字段深度受控,并能节省更多渲染工作

默认比较已经足够时不要增加比较器;它会成为组件接口的一部分,需要随新 prop 一起维护。

核心回答

先记住这个答案

arePropsEqual 接收旧 props 和新 props,只有它们会产生相同输出和行为时才应返回 true。适合输入结构明确、比较成本可控,并且默认引用比较导致昂贵子树频繁重算的场景。比较范围必须包含函数 prop:新函数可能捕获新的权限或业务状态,忽略它会让旧闭包继续响应点击。比较器自身应当纯粹,且需要用真实生产负载证明比较比重新渲染更划算。

  • true 表示允许复用旧输出和行为
  • 函数 prop 也属于必须比较的输入
  • 有界数据比较仍需要测量成本

从输出与行为的契约推导比较字段

下面的列表只读取候选人的 id、name 和 onPick。即便父级重新构建内容相同的数组,这几个字段仍可以有界地逐项比较;只比较数组长度显然不够,同长度列表也可能换人或改名。

onPick 同样不能省略。假设父级把录取回调换成另一个团队的处理函数,列表文字完全不变,用户下一次点击却应进入新的团队流程。旧函数的源码看起来相同,也无法证明捕获的数据相同。

把显示字段与点击回调都纳入比较TSX
import { memo } from 'react';

type Person = Readonly<{ id: string; name: string }>;
type Props = {
  people: readonly Person[];
  onPick: (id: string) => void;
};

function PeopleList({ people, onPick }: Props) {
  return <ul>{people.map(person => <li key={person.id}>
    <button onClick={() => onPick(person.id)}>{person.name}</button>
  </li>)}</ul>;
}

export default memo(PeopleList, (previous, next) =>
  previous.onPick === next.onPick &&
  previous.people.length === next.people.length &&
  previous.people.every((person, index) =>
    person.id === next.people[index].id &&
    person.name === next.people[index].name
  )
);

这个比较器只适用于示例声明的三类输入。增加头像地址、禁用状态或其他 prop 时,需要同步检查比较契约;不能直接把这段代码当作任意业务列表的通用等价判断。

可变输入会抹掉比较需要的过去

若父级直接修改同一个 person 对象,previous 与 next 可能指向已经被修改的同一份内容,比较器无法从中恢复旧名字。深比较也不能凭空找回历史值,因此不可变更新是输入设计的一部分。

比较器中不要调用 setter、写缓存系统或发送请求。React 可能重新执行或放弃一次渲染,比较逻辑必须只依据输入返回判断。读取会随时间变化的外部变量同样会让这个判断难以推理。

把比较耗时和节省的工作放在一起算

逐项扫描列表的时间随人数增长,复杂嵌套结构的深比较成本还可能更高。如果列表渲染本身很便宜,每次父级更新都做比较反而增加工作。应在生产模式、代表性数据规模和真实操作下比较两种实现。

正确性验收至少包含字段修改、顺序调整、成员增删和回调替换。性能验收再观察交互延迟及子树耗时,不能从一次日志变少推导出系统一定更快,也不应为了漂亮的次数牺牲新回调的生效。

回答前,多想一步

容易答错的地方

忽略函数 prop 以提高命中率
忽略函数可能让子树保持旧事件处理器,从而使用旧租户、旧筛选或旧权限。应比较函数身份,必要时让父级正确稳定回调;不能通过比较器阻止新业务语义传播。
用 JSON.stringify 当通用比较
序列化会忽略某些值,无法表达函数闭包,还会分配字符串并遍历数据。即使某个纯数据对象可以序列化,也要单独证明业务等价与成本,不能把序列化相等视为任意 props 的安全替代品。
试着用自己的话回答

面试官还会怎么问?

arePropsEqual 与 shouldComponentUpdate 返回值一样吗?

方向相反。arePropsEqual 返回 true 表示新旧输入等价,可以考虑跳过;shouldComponentUpdate 返回 true 表示需要更新。迁移已有类组件比较逻辑时应先用具体输入列出预期,再决定是否取反及比较哪些字段。

比较器能挡住本地 state 更新吗?

不能,它是 props 更新的比较边界。组件自己的 state 或所消费的 Context 变化有独立更新路径;如果这些更新昂贵,应调整状态位置或订阅范围,而不是继续加大 props 比较器。

回调每次都是新函数怎么办?

先判断子树是否确实昂贵,再让父级通过完整依赖的 useCallback 保留无关更新时的函数身份。回调真实依赖改变后重新渲染是正确结果,也可以重构接口让必要输入更明确。

从一道题,走向一组知识

把知识连起来

性能与并发

给 memo 子组件传内联对象或函数为什么会失去优化机会?

在父级修复无关引用变化,减少使用比较器的必要。

性能与并发

React.memo、PureComponent 和 shouldComponentUpdate 有什么区别?

对照类组件的浅比较和相反方向的更新判断。

性能与并发

什么情况下 React.memo 没有收益,甚至增加成本?

评估比较成本是否大于实际节省的子树工作。

参考资料

  • React 官方:memo 自定义比较函数

示例用于理解所注明的运行环境与边界;延伸学习可结合原文中的更多案例。

本题目录
  1. 先记住这个答案
  2. 从输出与行为的契约推导比较字段
  3. 可变输入会抹掉比较需要的过去
  4. 把比较耗时和节省的工作放在一起算
  5. 容易答错的地方
  6. 面试官还会怎么问
  7. 把知识连起来
读懂,再试着讲出来

先看核心答案,再读代码。最后展开追问,检查自己有没有遗漏边界。

试着回答追问
浏览全部面试题理解原理,也关注真实的使用场景。回到顶部 ↑