React Hooks/Redux Toolkit两类Redux Store切片选择效率对比
两种Redux学生切片选择方式的性能差异分析
首先明确核心判断的基础:React-Redux 的 useSelector 默认使用 Object.is 做相等性校验,只有当选择器返回的值和上一次相比严格不相等时,才会触发组件重渲染,两者的所有性能差异都围绕这个运行机制展开。
方式1:组件内直接写内联选择器的运行表现
- 每次 store 任意状态更新、或是组件自身因为 props/本地 state 变化触发重渲染时,这个匿名的内联选择器都会重新从根 state 上读取
state.students切片 - 因为项目用的是 Redux Toolkit,所有切片的引用都是 Immer 维护的不可变结构:只要没有针对 students 切片的 action 触发更新,这个切片的内存引用永远是稳定的
- 这种前提下,内联选择器每次拿到的都是同一个稳定的 students 引用,经过 Object.is 校验后,不会因为这个选择器本身触发组件的额外重渲染
- 额外的小开销:如果这个组件被多个页面、多个实例复用,每个组件实例里的内联选择器都是独立的函数,没有共享缓存的能力,每次都要独立跑一次读取逻辑
方式2:透传逻辑的 memoized 选择器的运行表现
首先要明确:这里写的 createSelector 逻辑是完全透传的——输入是 state.students,输出原封不动返回拿到的 students 值,没有做任何形式的派生计算。
- memoized 选择器的核心缓存机制是:只要上游输入参数(也就是读取到的
state.students)引用没变,就直接返回上一次缓存的输出值,不会重复执行内部的转换逻辑 - 因为这里没有实际的转换逻辑,它最终返回的 students 引用和内联选择器直接从 state 读出来的引用完全一致,和上一次值做 Object.is 校验的结果也和方式1没有区别
- 这个场景下 createSelector 的缓存层是完全空转的:memoized selector 本来的价值是避免重复执行派生计算(比如把字典转成数组、筛选符合条件的学生、跨切片聚合数据),但这里没有计算逻辑,缓存没有实际发挥作用
- 仅有的微弱优势:这个选择器是模块级的单例,所有引用它的组件实例都会共享同一个选择器的缓存,当大量组件同时订阅 students 切片时,不需要每个组件单独跑一次读取流程,能减少重复的函数执行开销,但因为没有计算逻辑,这个收益非常有限
实际场景下的差异总结
- 如果 students 切片本身没有发生变更:两种方式触发组件重渲染的概率完全一致,都不会因为选择器的原因产生多余渲染。这里要纠正一个常见误区:很多人以为内联选择器每次都会返回新对象才会有性能问题,但这里我们读取的是 RTK 维护的稳定切片本身,不是在选择器内部临时生成的新对象,所以不存在这个问题
- 如果 students 切片本身被 action 更新了:两种方式都会检测到引用变化,触发组件的正常重渲染,没有性能差异
- 只有当后续需求升级,需要对 students 切片做派生计算(比如过滤掉毕业的学生、把字典转成按分数排序的数组)时,memoized 选择器的价值才会真正体现:这时候如果还用方式1的内联选择器写派生逻辑,每次 store 任意更新都会重新生成新的派生结果,哪怕原始 students 切片完全没变,也会因为每次返回新的数组/对象引用触发组件不必要的重渲染;而 memoized 选择器会在输入切片没变时直接返回缓存的旧派生结果,从根源上避免这类多余渲染
开发注意:不要为了“凑齐最佳实践”给所有直接读取原始切片的逻辑套一层 createSelector,这种纯透传式的 memoization 没有实际收益,反而会增加不必要的代码理解成本
内容的提问来源于stack exchange,提问作者Cesar N Mejia Leiva
相关产品推荐
相关产品推荐

