自定义合并策略:深入ngrx-store-localstorage的mergeReducer与deepmerge数组覆盖机制
2026/9/19 4:51:22 网站建设 项目流程

自定义合并策略:深入ngrx-store-localstorage的mergeReducer与deepmerge数组覆盖机制

【免费下载链接】ngrx-store-localstorageSimple syncing between @ngrx store and local storage项目地址: https://gitcode.com/gh_mirrors/ng/ngrx-store-localstorage

ngrx-store-localstorage 是面向 @ngrx/store 的状态持久化插件,负责在 NgRx 状态仓库与浏览器 localStorage 之间双向同步,实现页面刷新后状态自动恢复。本文聚焦两个核心机制:mergeReducer自定义合并策略,以及默认合并中 deepmerge 的数组覆盖行为,帮新手快速搞懂状态"复活"的完整链路。

🧩 先搞懂合并发生在哪:localStorageSync 的三步包装

加载localStorageSync配置后,每个 action 都会先经过库的包装层,再进入你的业务 reducer,调用顺序在projects/lib/src/lib/index.ts#L336-L351

  1. 用你的 reducer 算出默认状态,得到nextState
  2. 执行mergeReducer(nextState, rehydratedState, action),把从 localStorage 读出的水合状态合并进去;
  3. 合并结果再交给你的真实 reducer 产出最终状态。

也就是说:无论默认还是自定义合并策略,都发生在业务 reducer 之前,它决定的是 reducer 看到的"初始状态"。

⚙️ 默认合并策略:deep merge 深合并 + 数组覆盖

不传mergeReducer时,库自动使用内置的defaultMergeReducer,源码见projects/lib/src/lib/index.ts#L277-L299,三个关键点:

  • 时机:仅当 action 为@ngrx/store/init@ngrx/store/update-reducers时才合并,其余动作原样放行;
  • 方式:调用deepmerge做深合并——对象逐键合并,localStorage 里存的属性覆盖默认值,没存的属性保留默认值;
  • 数组覆盖:给 deepmerge 传入了自定义arrayMerge回调overwriteMerge,其实现只有一行——直接返回源数组(即存储中的数组)。

📋 为什么数组要"覆盖":deepmerge 默认拼接的坑

deepmerge 对数组的默认行为是把两边数组拼接(先 destination 后 source)。假设:

  • 代码里的默认状态:tabs: [1, 2]
  • localStorage 里已存的快照:tabs: [1, 2, 3]

若按默认行为合并,刷新后会得到[1, 2, 1, 2, 3]——列表凭空多出一堆重复项。而overwriteMerge让数组遵循"最后一次写入获胜"的语义:存储是最新快照,数组整体替换代码默认值,得到干净的[1, 2, 3]。对象仍走深合并,"存的覆盖、没存的保留"依然成立。这正是刷新后列表重复问题的根源,也是默认策略要显式处理数组的原因。

🛠️ 如何写自定义 mergeReducer:签名与常见场景

配置接口中mergeReducer的类型定义在projects/lib/src/lib/index.ts#L375-L385

mergeReducer?: (state: any, rehydratedState: any, action: any) => any;
  • state:当前内存中的初始状态(reducer 的默认状态)
  • rehydratedState:按keys配置从 localStorage 读出的水合状态
  • action:当前动作,可按动作类型分支处理

返回值就是即将交给业务 reducer 的初始状态。常见使用场景:

  1. 部分覆盖:只接受存储中的某些字段,忽略其余;
  2. 混合策略:某些 feature 全量深合并,某些只取指定字段;
  3. 数据迁移:合并阶段把旧版存储结构转换成新版结构。

🔧 实战示例:按 feature 混合合并

官方测试spec/index.spec.ts#L751-L800给出了典型示例:feature1feature2分片存储,自定义合并函数对两者采用不同策略:

const mergeReducer = (state, rehydratedState, action) => ({ ...state, feature1: { slice11: rehydratedState.feature1.slice11 }, // 只取一个字段 feature2: deepmerge(state.feature2, rehydratedState.feature2), // 完整深合并 });

配合localStorageSync({ keys: [...], rehydrate: true, mergeReducer })后,最终状态里feature1只保留了存储值中的slice11feature2则完成了完整深合并。可见合并策略完全交给开发者,"字段挑选"与"deepmerge"可以在同一应用里自由搭配。

⚡ 总结:合并策略速查清单

  • 不传mergeReducer?默认策略 = 深合并 + 数组整体覆盖,多数场景直接可用;
  • 想精细控制"恢复哪些字段"?传入自定义函数,返回合并后的初始状态即可;
  • 遇到恢复后数组重复?检查是否误用了 deepmerge 的默认arrayMerge(拼接);
  • 合并只发生在 init / update-reducers 动作、且在业务 reducer 之前,不会干扰后续任何 action。

更多配置项说明可参考 README.md 中mergeReducer的官方描述(第 98 行)。

【免费下载链接】ngrx-store-localstorageSimple syncing between @ngrx store and local storage项目地址: https://gitcode.com/gh_mirrors/ng/ngrx-store-localstorage

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询