
Redux性能优化5大技巧用Selector选择器与记忆化解决组件重复渲染【免费下载链接】reduxA JS library for predictable global state management项目地址: https://gitcode.com/gh_mirrors/re/reduxRedux 是用于可预测全局状态管理的 JS 库而Redux 性能优化的核心往往在于如何用Selector 选择器与**记忆化Memoization**解决组件重复渲染的问题。本文面向新手用 5 个可直接落地的技巧带你避开每发一个 action 就全页重渲染的性能陷阱让 React Redux 应用保持流畅 ⚡一、先搞清楚为什么组件会多余地重复渲染在 Redux 的单向数据流中每次dispatch一个 action订阅了 store 的组件都会重新执行选择器函数。React-Redux 用引用比较来判断是否需要重渲染——如果选择器每次都返回一个新对象 / 新数组哪怕数据内容一模一样组件也会被强制重渲染。这是 Redux 应用最常见的性能问题。官方 FAQ 也明确指出使用记忆化的选择器函数是重要的性能考量Use of memoized selector functions is also an important performance consideration。 延伸阅读docs/faq/Performance.md二、技巧1用 Selector 选择器封装状态读取只取最小数据选择器Selector是任何接收 Redux state 并返回其中某个值的函数相当于对状态的一次查询。它有三个好处封装只有 reducer 和 selector 知道状态结构改动只改一处♻️复用多个组件共享同一段取值逻辑最小化选择组件只订阅真正需要的字段而不是整个 slice官方风格指南建议选择器统一命名为selectXxx如selectTodoById、selectVisibleTodos并与 reducer 一起定义在 slice 文件中 docs/style-guide/style-guide.md// 直接查找——普通函数即可无需记忆化 export const selectTodos state state.todos记住第一条黄金法则状态存得越少越好能推导的值过滤、求和、排序就不要存进 state而是用选择器现场推导。详见 docs/usage/deriving-data-selectors.md三、技巧2别让选择器每次都返回新引用这是新手最容易踩的坑 ⚠️ 看下面这个 Todo 列表组件function TodoList() { // ❌ filter() 每次都产生新数组引用 → 每次 action 后都重渲染 const completedTodos useSelector(state state.todos.filter(todo todo.completed) ) }filter()、map()、对象展开{...}这类操作每次都生成新引用导致组件在每个 action 后都重渲染即使数据根本没变。React-Redux 在开发模式下还会主动警告你Selector unknown returned a different result when called with the same parameters. This can lead to unnecessary rerenders.判断口诀选择器返回的是直接查找的原始引用或基础类型数字、字符串、布尔→ 安全返回新数组/新对象→ 必须处理。四、技巧3用 createSelector 记忆化昂贵选择器记忆化是一种缓存输入没变就直接返回上次的结果不重复计算。Redux 生态的标准工具是 Reselect 的createSelectorRedux Toolkit 已内置导出。下面这张 Profiler 截图展示了修复前的灾难现场只是刷新了通知列表UserPage却因为非记忆化的filter选择器被白白重渲染了修复方式——把selectPostsByUser改写为记忆化选择器export const selectPostsByUser createSelector( // 输入选择器只负责取原始值 [selectAllPosts, (state, userId) userId], // 输出函数负责转换计算仅当输入变化时才重跑 (posts, userId) posts.filter(post post.user userId) )原理很简单createSelector会对比各输入选择器的结果只要posts和userId都没变就直接返回上次的缓存数组引用不变组件跳过渲染。 完整案例含 Reselect 用法细节与常见错误docs/usage/deriving-data-selectors.md常见错误输出函数直接返回输入todos todos等于没做记忆化另外createSelector默认缓存只保留最近一组输入多组件用不同参数复用同一选择器时需要用选择器工厂为每个组件创建独立实例。五、技巧4状态规范化让按 ID 查数据变成 O(1)当数据是长数组时array.find()逐个遍历找某个 ID 会白白消耗 CPU。官方 FAQ 建议把状态存成**规范化Normalized**形状{ ids: [], entities: {} }——ID 作键的查找表 一个排序好的 ID 数组。Redux Toolkit 的createEntityAdapter帮你自动管理这套结构并提供现成的selectAll/selectById选择器const postsAdapter createEntityAdapterPost({ sortComparer: (a, b) b.date.localeCompare(a.date) }) const { selectAll: selectAllPosts, selectById: selectPostById } postsAdapter.getSelectors(state state.posts)好处有三 按 ID 直接取值无需遍历 每条数据只存一份省内存 ID 数组只在增删或排序变化时才更新天然利于减少重渲染 规范化设计思路docs/usage/structuring-reducers/NormalizingStateShape.md六、技巧5列表组件父只传 ID、子自选数据只渲染变化项React 的默认行为是父组件重渲染时会递归渲染所有子组件。所以点一下某条帖子的点赞按钮整条帖子列表里的所有子项都会跟着重渲染官方推荐的优化套路是列表父组件只选择ID 数组selectPostIdsID 没变就不重渲染每个列表项组件通过postId自己调用useSelector(selectPostById)取数据再配合React.memo包裹子组件确保 props 不变就不渲染优化后的 Profiler 截图——只有被点的那一个组件渲染了 完整步骤React.memo 选项、shallowEqual 比较等docs/tutorials/essentials/part-6-performance-normalization.md七、记忆化平衡术不是所有选择器都要缓存过度优化同样是坑 ❌ 官方给出的判断标准场景是否记忆化直接取值state state.todos❌ 不要普通函数即可推导但返回稳定值求和、计数返回数字 可选每次返回新引用map/filter生成新数组✅ 必须给每个字段都写一个选择器、给每个选择器都套一层createSelector只会增加维护成本让 Redux 变成Java getter 地狱。只对返回新引用或计算昂贵的选择器做记忆化这是最实用的平衡点。八、常见错误速查清单 ❌ 在useSelector里直接filter/map/ 展开对象 → 每次 action 后新引用 → 用createSelector记忆化❌ 选择器输出函数直接返回输入 → 记忆化失效❌ 把整个大 slice 一股脑选进组件 → 只选需要的字段❌ 多个组件共用一个带参数的记忆化选择器却传不同参数 → 缓存永远命中不了 → 用选择器工厂❌ 在 reducer 里做深拷贝deep clone→ 所有字段引用全变触发大面积重渲染不可变更新只需浅拷贝受影响的路径九、总结#技巧解决的问题1Selector 选择器封装、最小化选择状态结构散落、组件订阅过多数据2避免选择器返回新引用每个 action 后组件无脑重渲染3createSelector记忆化昂贵计算重复执行 新引用强制渲染4状态规范化Entity Adapter大数组遍历查找慢、内存冗余5父传 ID 子自选数据 React.memo长列表一人点赞全员重渲Redux 本身并不慢——状态树只是一个纯 JS 对象真正拖慢应用的是不必要的重复渲染。掌握选择器与记忆化这两件武器你的 Redux 应用就能在数据频繁更新的场景下依然保持丝滑 ✨延伸阅读选择器完整指南docs/usage/deriving-data-selectors.md性能与规范化教程docs/tutorials/essentials/part-6-performance-normalization.mdRedux 性能 FAQdocs/faq/Performance.mdstore 与 reducer 源码src/createStore.ts、src/combineReducers.ts【免费下载链接】reduxA JS library for predictable global state management项目地址: https://gitcode.com/gh_mirrors/re/redux创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考