Reselect 中使用闭包实现带参选择器的性能陷阱解析
发布时间 - 2025-12-26 00:00:00 点击率:次在 reselect 中,用闭包动态创建 memoized selector 会导致每次渲染都新建 selector 实例,使缓存失效、重复执行计算逻辑,带来显著性能损耗。
Reselect 的核心优势在于其基于输入参数的智能缓存机制(即 recomputations 和 memoized 结果复用)。但这一机制的前提是:selector 实例必须稳定复用——即同一 selector 函数在多次调用中保持引用一致,才能正确比对前序输入并命中缓存。
❌ 闭包方式:每次渲染都新建 selector(性能隐患)
const selectOrdersByCustomer = customerId =>
createSelector(
state => state.orders,
orders => {
console.count('⚠️ output selector executed'); // 每次都会触发!
return orders.filter(order => order.customerId === customerId);
}
);
// 在组件中:
const orders = useSelector(selectOrdersByCustomer(customerId));问题在于:selectOrdersByCustomer(customerId) 每次调用都返回一个全新 selector 实例。即使 customerId 相同,useSelector 接收到的是不同函数引用,Reselect 无法关联历史缓存;更严重的是,该 selector 内部的 state => state.orders 输入选择器也随 selector 实例重建而“重置”,导致缓存完全失效。
如实验日志所示:
⚠️ output selector executed: 1 ⚠️ output selector executed: 2 ⚠️ output selector executed: 3 → 返回结果不相等(s1 !== s2),recomputations 累计为 3
这不仅浪费 CPU(重复 filter),还可能引发不必要的组件重渲染(尤其当返回新数组时,浅比较失败)。
✅ 标准方式:参数化 selector(推荐实践)
const selectOrdersByCustomer = createSelector(
state => state.orders,
(state, customerId) => customerId, // 显式声明第二个输入选择器
(orders, customerId) => {
console.count('✅ memoized output selector'); // 仅首次或输入变更时执行
return orders.filter(order => order.customerId === customerId);
}
);
// 组件中稳定复用同一 selector 实例:
const orders = useSelector(state => selectOrdersByCustomer(state, customerId));✅ 此写法中,selectOrdersByCustomer 是单例函数,useSelector 始终接收相同引用;Reselect 能正确追踪 (state.orders, customerId) 的组合变化,仅当 state.orders 数组变更 或 customerId 改变时才重新计算,并复用上一次结果。
实验验证:
✅ memoized output selector: 1 → recomputations() === 1,且 x1 === x2 === x3(返回同一数组引用)
⚠️ 注意事项与优化建议
- 避免在 render 中调用工厂函数:如 selectXxx(param)、makeSelector(param) 等模式均会破坏 selector 稳定性;
- 若需多参数,统一通过 (state, props) 传入:Reselect v5+ 支持 props 作为第二参数,与 Redux Toolkit 的 createSelector 兼容;
- 结合 useMemo 非必要:不要试图用 useMemo(() => selectXxx(id), [id]) “优化”闭包 selector——这仍会生成新函数,且增加额外开销;
- 检查 selector 引用稳定性:可在组件内 useEffect(() => { console.log(selectOrdersByCustomer) }, []) 验证是否始终为同一函数。
总之,Reselect 的性能收益高度依赖 selecto
r 实例的持久性。坚持使用参数化(而非闭包工厂)模式,是保障 memoization 有效、避免隐式性能退化的关键实践。
# red
# Filter
# 闭包
# console
# 选择器
# 复用
# 的是
# 这一
# 首次
# 可在
# 第二个
# 所示
# 而非
# 时才
相关栏目:
【
网站优化151355 】
【
网络推广146373 】
【
网络技术251813 】
【
AI营销90571 】
相关推荐:
php静态变量怎么调试_php静态变量作用域调试技巧【解答】
实现点击下箭头变上箭头来回切换的两种方法【推荐】
历史网站制作软件,华为如何找回被删除的网站?
Python图片处理进阶教程_Pillow滤镜与图像增强
Win11怎么查看显卡温度 Win11任务管理器查看GPU温度【技巧】
javascript和jQuery中的AJAX技术详解【包含AJAX各种跨域技术】
如何基于PHP生成高效IDC网络公司建站源码?
Laravel怎么判断请求类型_Laravel Request isMethod用法
如何用已有域名快速搭建网站?
千库网官网入口推荐 千库网设计创意平台入口
潮流网站制作头像软件下载,适合母子的网名有哪些?
Laravel如何配置.env文件管理环境变量_Laravel环境变量使用与安全管理
如何续费美橙建站之星域名及服务?
齐河建站公司:营销型网站建设与SEO优化双核驱动策略
java获取注册ip实例
LinuxShell函数封装方法_脚本复用设计思路【教程】
Laravel如何实现一对一模型关联?(Eloquent示例)
JavaScript如何实现音频处理_Web Audio API如何工作?
Laravel如何自定义错误页面(404, 500)?(代码示例)
如何在Windows服务器上快速搭建网站?
如何在Tomcat中配置并部署网站项目?
Laravel storage目录权限问题_Laravel文件写入权限设置
Laravel如何使用Gate和Policy进行授权?(权限控制)
如何快速搭建安全的FTP站点?
如何快速生成橙子建站落地页链接?
微信小程序 闭包写法详细介绍
jQuery validate插件功能与用法详解
详解免费开源的.NET多类型文件解压缩组件SharpZipLib(.NET组件介绍之七)
今日头条AI怎样推荐抢票工具_今日头条AI抢票工具推荐算法与筛选【技巧】
Laravel怎么实现API接口鉴权_Laravel Sanctum令牌生成与请求验证【教程】
绝密ChatGPT指令:手把手教你生成HR无法拒绝的求职信
Laravel如何使用Seeder填充数据_Laravel模型工厂Factory批量生成测试数据【方法】
猎豹浏览器开发者工具怎么打开 猎豹浏览器F12调试工具使用【前端必备】
phpredis提高消息队列的实时性方法(推荐)
如何在阿里云服务器自主搭建网站?
Laravel如何使用Spatie Media Library_Laravel图片上传管理与缩略图生成【步骤】
Laravel如何安装Breeze扩展包_Laravel用户注册登录功能快速实现【流程】
详解免费开源的DotNet二维码操作组件ThoughtWorks.QRCode(.NET组件介绍之四)
Laravel Livewire是什么_使用Laravel Livewire构建动态前端界面
如何在宝塔面板中创建新站点?
php嵌入式断网后怎么恢复_php检测网络重连并恢复硬件控制【操作】
Laravel怎么进行数据库回滚_Laravel Migration数据库版本控制与回滚操作
JavaScript常见的五种数组去重的方式
Win11怎么恢复误删照片_Win11数据恢复工具使用【推荐】
Laravel项目如何进行性能优化_Laravel应用性能分析与优化技巧大全
Laravel Debugbar怎么安装_Laravel调试工具栏配置指南
制作企业网站建设方案,怎样建设一个公司网站?
Laravel如何处理跨站请求伪造(CSRF)保护_Laravel表单安全机制与令牌校验
Laravel API资源类怎么用_Laravel API Resource数据转换
详解CentOS6.5 安装 MySQL5.1.71的方法

