首页/新闻资讯/正文详情

NgRx Data Entity Reducer:实体缓存主 Reducer、集合 Reducer 与 MetaReducer 定制机制详解

发布时间:2026/9/25 11:09:38 来源:云帆数科 栏目:资讯中心
NgRx Data Entity Reducer:实体缓存主 Reducer、集合 Reducer 与 MetaReducer 定制机制详解
前端状态管理【免费下载链接】platformReactive State for Angular项目地址https://gitcode.com/gh_mirrors/pl/platform点击查看免费下载Entity Reducer 是 NgRx Data 实体缓存EntityCache的主 reducer负责把每一条EntityAction路由到对应实体集合的 reducer并在需要时初始化集合状态。本篇基于官方指南 entity-reducer.md结合ngrx/data包的源码modules/data/src/reducers/目录逐层解析主 reducer 的分发逻辑、EntityCollectionReducer注册表、默认集合 reducer 的EntityOp分派机制、集合状态初始化以及 EntityCache 级动作与两级 MetaReducer 定制点帮助你从原理到扩展点完整掌握 NgRx Data 的缓存更新链路。需要说明ngrx/data包目前处于维护模式maintenance mode变更仅限于关键缺陷修复官方不再为其增加新特性。一、Entity Reducer 总览EntityCacheReducerFactory 是核心Entity Reducer 是存储实体缓存中所有实体集合的master reducer。ngrx/data并没有一个具名的 entity reducer 类型而是依赖EntityCacheReducerFactory.create()方法生产该 reducer。它的签名本质上是一个 NgRx 的ActionReducerEntityCache, EntityAction输入EntityCache状态按实体类型名索引的EntityCollection哈希和一个EntityAction动作输出新的EntityCache状态。EntityCache的类型定义非常简洁就是一个以集合名为键的映射见 entity-cache.tsexport interface EntityCache { // Must be any since we dont know what type of collections we will have [name: string]: EntityCollectionany; }这个 reducer 只响应两类动作EntityCache 级动作较少见针对某个实体集合的EntityAction通常情况。其他所有Action都会被忽略reducer 直接原样返回传入的state。从源码看EntityCacheReducerFactory.create() 返回的 reducer 先对EntityCacheAction常量做 switch 匹配处理缓存级动作然后判断// Apply entity collection reducer if this is a valid EntityAction for a collection const payload action.payload; if (payload payload.entityName payload.entityOp !payload.error) { return this.applyCollectionReducer(entityCache, action as EntityAction); } // Not a valid EntityAction return entityCache;也就是说当前源码以payload.entityName属性作为这是一个 EntityAction的判据指南中写作entityType与早期版本的命名一致并且会跳过携带error的 payload。主 reducer 的主要工作可归纳为五步从state中提取动作所指向实体类型的EntityCollection必要时创建一个新的、已初始化的实体集合获取或创建该实体类型的EntityCollectionReducer用该集合和动作调用entity collection reducer用entity collection reducer返回的新集合替换EntityCache中原集合。上述流程对应源码中的 applyCollectionReducerprivate applyCollectionReducer(cache: EntityCache {}, action: EntityAction) { const entityName action.payload.entityName; const collection cache[entityName]; const reducer this.entityCollectionReducerRegistry.getOrCreateReducer(entityName); let newCollection: EntityCollection; try { newCollection collection ? reducer(collection, action) : reducer(this.entityCollectionCreator.create(entityName), action); } catch (error: any) { this.logger.error(error); action.payload.error error; } return action.payload.error || collection newCollection! ? cache : { ...cache, [entityName]: newCollection! }; }几个值得注意的实现细节集合不存在时通过注入的EntityCollectionCreator现场创建一个初始集合集合 reducer 抛错时不会让 store 崩溃而是把错误挂到action.payload.error并记录日志只有当集合引用真正变化时才返回新的缓存对象{ ...cache, [entityName]: newCollection }否则复用原cache引用——这是 NgRx 中减少无谓重渲染的常见做法。二、EntityCollectionReducers 与注册表EntityCollectionReducer把actions应用到 NgRx store 中EntityCache里的EntityCollection上。其类型定义见 entity-collection-reducer.tsexport type EntityCollectionReducerT any ( collection: EntityCollectionT, action: EntityAction ) EntityCollectionT;每种实体类型永远有一个 reducerEntityCollectionReducerRegistry维护一个注册表entity-collection-reducer-registry.ts。当它找不到某实体类型的 reducer 时会借助注入的EntityCollectionReducerFactory创建一个并把它注册起来供下次复用getOrCreateReducerT(entityName: string): EntityCollectionReducerT { let reducer: EntityCollectionReducerT this.entityCollectionReducers[entityName]; if (!reducer) { reducer this.entityCollectionReducerFactory.createT(entityName); reducer this.registerReducerT(entityName, reducer); this.entityCollectionReducers[entityName] reducer; } return reducer; }注册自定义集合 reducer你可以为某个实体类型创建自定义 reducer并通过EntityCollectionReducerRegistry.registerReducer()直接注册// 注册单个 registry.registerReducer(Hero, myHeroReducer); registry.registerReducer(Villain, myVillainReducer); // 批量注册reducerMap 是以实体类型名为键的 reducer 哈希 registry.registerReducers({ Hero: myHeroReducer, Villain: myVillainReducer, });对应源码中registerReducers只是遍历键名逐个调用registerReducer并且每次注册都会先把 reducer 包进 meta-MetaReducer见 MetaReducer 章节再写入注册表registerReducerT(entityName: string, reducer: EntityCollectionReducerT): EntityCollectionReducerT { reducer this.entityCollectionMetaReducer(reducer as any); return (this.entityCollectionReducers[entityName.trim()] reducer); }相关行为在 entity-collection-reducer-registry.spec.ts 中有测试覆盖。三、默认 EntityCollectionReducer按 EntityOp 分派EntityCollectionReducerFactory创建的默认 reducer 充分利用了 NgRxEntityAdapter的能力并由应用的entity metadata引导。默认 reducer 的核心逻辑极其精简return function entityCollectionReducer( collection: EntityCollectionT, action: EntityAction ): EntityCollectionT { const reducerMethod methods[action.payload.entityOp]; return reducerMethod ? reducerMethod(collection, action) : collection; };它以EntityAction.payload.entityOp为索引从一张EntityOp→ 处理方法的映射表中找到对应方法并调用找不到方法就直接返回原集合——所以大量EntityOp值是被忽略的集合保持不变。EntityCollectionReducerMethods 中这张映射表覆盖了三大类操作类别典型 EntityOp行为特征查询类QUERY_ALL、QUERY_BY_KEY、QUERY_MANY及各自的_ERROR/_SUCCESS更新集合数据并翻转loaded/loading标志持久化类SAVE_ADD_ONE、SAVE_UPDATE_MANY、SAVE_DELETE_MANY、SAVE_UPSERT_*等面向 NgRx Data 的persist$效果通常只更新集合数据或仅翻转loading标志真正落库由 effect 完成纯缓存类ADD_ONE、ADD_MANY、ADD_ALL、REMOVE_ONE、UPDATE_MANY、UPSERT_ONE等直接增删改集合中的实体不可变性是核心原则这些 reducer 并不会真正修改原集合或其中的对象而是复制集合、只更新副本中的对象——这与 redux/NgRx 模式的不可变状态要求一致。默认集合 reducer 实际执行了哪些缓存变更操作可以参考 NgRx Entity 的 EntityAdapter 集合方法。而默认 reducer 究竟如何工作的最终权威依据是EntityCollectionReducerFactory类及其测试entity-collection-reducer.spec.ts。四、初始化集合状态NgRxDataModule会向 NgRx Data store 添加一个空的EntityCache其中尚无任何集合。当主EntityReducer在state中找不到动作所指向实体类型的集合时它会借助注入到EntityCacheReducerFactory中的EntityCollectionCreator创建一个新初始化集合。EntityCollectionCreator.create() 的逻辑是createT any, S extends EntityCollectionT EntityCollectionT(entityName: string): S { const def this.entityDefinitionService this.entityDefinitionService.getDefinitionT(entityName, false); const initialState def def.initialState; return S(initialState || createEmptyEntityCollectionT(entityName)); }creator优先返回该实体EntityDefinition中initialState所定义的初始化集合若该实体类型没有definition或 definition 中没有initialState属性则返回一个空集合骨架createEmptyEntityCollectionids: []、entities: {}、loaded/loading均为false、空的changeState随后entity reducer把这个新集合作为entity collection reducer的state参数传入。这也意味着为实体提供EntityDefinition含initialState是定制初始集合状态的官方入口实体元数据定义详见 entity-metadata 指南。五、定制 Entity Reducer 行为的三个层级你可以按粒度由小到大逐层替换替换某个集合 reducer通过注册自定义替代EntityCollectionReducerRegistry.registerReducer/registerReducers替换默认集合 reducer 工厂向 EntityCollectionReducerFactory 提供自定义替代如通过 DI 覆盖 useClass替换主 Entity Reducer向EntityCacheReducerFactory提供自定义替代。不过更常见的诉求不是整体替换而是在某个集合 reducer 之前或之后追加一些额外的 reducer 逻辑——这时应考虑 MetaReducers。六、EntityCache 级动作少数动作直接作用于整个实体缓存。SET_ENTITY_CACHE会用动作 payload 中的对象替换整个缓存相当于把实体缓存重新初始化到一个已知状态MERGE_ENTITY_CACHE会用动作 payload 中存在的集合替换当前缓存中的对应集合其余集合保持不变。重要提示原文档强调MERGE_ENTITY_CACHE是替换replace当前缓存的集合而不是把 payload 中的实体合并merge进现有集合——不要望文生义。对应地当前仓库源码中缓存级动作定义在 entity-cache-action.ts包括EntityCacheAction.SET_ENTITY_CACHE动作类SetEntityCache注释明确写着危险但有用典型场景是在应用启动时从浏览器本地存储重新水合re-hydrateEntityCacheEntityCacheAction.MERGE_QUERY_SET动作类MergeQuerySet把一次多集合查询结果按MergeStrategy默认MergeStrategy.PreserveChanges合入各集合以及CLEAR_COLLECTIONS、LOAD_COLLECTIONS和SAVE_ENTITIES系列动作。需要注意这些动作不是EntityAction没有entityName/entityOppayload因此不会被主 reducer 的集合分派逻辑捕获而是在入口 switch 中被直接处理例如case EntityCacheAction.SET_ENTITY_CACHE: { // Completely replace the EntityCache. Be careful! return action.payload.cache; }entity-cache-dispatcher.ts 为这些动作提供了便捷的派发方法setEntityCache(cache, tag)、mergeQuerySet(querySet, mergeStrategy, tag)、clearCollections(collections, tag)、loadCollections(collections, tag)等并支持可选的tag参数用于从应用角度标识操作来源。离线与批量回滚场景这类缓存级动作非常适合支撑离线场景或同时对多个集合进行回滚订阅EntityServices.entityCache$选择器当缓存变化时将其序列化写入浏览器 local storage建议加几秒debounce以减少频繁写入应用重新启动时即使处于断网状态也可以派发SET_ENTITY_CACHE动作用本地存储恢复实体缓存的初始状态或在错误恢复、what-if 场景中派发MERGE_ENTITY_CACHE把选定的集合回滚到某个已知状态。SET_ENTITY_CACHE与缓存恢复动作的用法示例可参考 entity-cache-reducer.spec.ts 中 EntityCache-level actions 一节的测试注释中明确提到 re-hydrating from browser storage / offline-capable app。如果你希望创建并 reduce额外的、作用于整个缓存的动作应考虑下文介绍的 EntityCache MetaReducer。七、MetaReducersNgRx Data 的两级定制点ngrx/store支持MetaReducers它们可以检查流经 store 的动作并有可能改变 store 中的状态MetaReducer 是接受一个 reducer、返回一个 reducer的函数NgRx 把它们与其他 reducer 组合成责任链chain of responsibilityNgRx 调用 MetaReducer 返回的 reducer 时与调用任何 reducer 一样传入state和action它可以对 state 和 action 为所欲为记录日志、自行处理动作、委托给传入的 reducer、后处理更新后的 state或全部组合。提醒动作本身是不可变的不要修改 action需要改请克隆它。和所有 reducer 一样传入 MetaReducer 之 reducer 的 state 只是该 reducer 作用域内的那部分 store。NgRx Data 支持两级 MetaReducerEntityCache MetaReducer作用域为整个实体缓存EntityCollection MetaReducer作用域为某个特定集合。EntityCache MetaReducersEntityCache MetaReducer帮助你检查和作用于整个实体缓存的动作。例如你可以定义自定义动作并配合 EntityCache MetaReducer 同时更新多个集合。其 reducer 必须满足三条要求始终返回完整的实体缓存同步返回不能等待服务器响应绝不修改原 action需要改动请克隆。用法示例可参考 entity-data.module.spec.ts。注入侧NgRxDataModule/provideEntityData通过ENTITY_CACHE_META_REDUCERS多值注入令牌收集这些 MetaReducer见 constants.ts 与 provide-entity-data.ts 中对ENTITY_CACHE_META_REDUCERS/ENTITY_COLLECTION_META_REDUCERS的 provide 配置。Entity Collection MetaReducersEntity collection MetaReducer以entity collection reducer作为 reducer 参数返回一个新的entity collection reducer。新 reducer 收到的正是原本会传给原 reducer 的EntityCollection与EntityAction参数它可以记录动作日志把动作转换为同一集合的另一个动作调用原 reducer对原 reducer 的结果做后处理。新的 entity collection reducer 必须满足三条要求始终返回同一实体的EntityCollection同步返回不能等待服务器响应绝不修改原 action需要改动请克隆。与 Store MetaReducers 的区别entity collection MetaReducer虽然仿照 NgRx Store 的MetaReducerStore MetaReducer建模但有几个关键差异Store MetaReducer面向的是store reducer它包装 store reducer、看到所有动作、可以更新其作用域内的任意状态但 Store MetaReducer既看不到也包装不了entity collection reducer——这些集合 reducer 位于注册在 NgRx Data feature 中的EntityCache Reducer内部entity collection MetaReducer则专注于对单个目标实体集合的操作它包装所有 entity collection reducer注意它无法访问其他集合、entity cache或 store 中的任何其他状态。如果你需要跨集合的 MetaReducer请改用 EntityCache MetaReducer。向工厂提供 Entity MetaReducers创建一个或多个entity collection MetaReducers放入一个数组然后在导入NgRxDataModule或调用provideEntityData的位置用ENTITY_COLLECTION_META_REDUCERS注入令牌提供这个数组。EntityCollectionReducerRegistry会注入该数组并用 NgRx 的compose把这一组 MetaReducer 组合成单一的meta-MetaReducer数组中靠前的MetaReducer 包装靠后的// EntityCollectionReducerRegistry 构造函数节选 this.entityCollectionMetaReducer compose.apply( null, entityCollectionMetaReducers || [] ) as any;当工厂注册一个EntityCollectionReducer包括它自行创建的那些时会先把它包进meta-MetaReducer再写入注册表。此后所有派发到 store 的EntityActions在进出某个实体专属 reducer 的过程中都会经过这层包装。如何创建并提供entity collection MetaReducers的完整示例可参考 entity-cache-reducer.spec.ts。八、小结NgRx Data 的 Entity Reducer 体系是一台两级路由机器主 reducerEntityCacheReducerFactory.create()先处理缓存级动作SET_ENTITY_CACHE等再把EntityAction按entityName路由集合 reducer由EntityCollectionReducerFactory创建、EntityCollectionReducerRegistry注册与缓存按EntityOp分派到基于EntityAdapter的具体方法集合初始化由EntityCollectionCreator结合EntityDefinition.initialState完成定制有三个替换层级集合 reducer / 集合工厂 / 主 reducer 工厂加两个 MetaReducer 作用域EntityCache 级、EntityCollection 级全部通过 DI 令牌ENTITY_CACHE_META_REDUCERS、ENTITY_COLLECTION_META_REDUCERS或注册表 API 注入。关键源码入口entity-cache-reducer.ts、entity-collection-reducer.ts、entity-collection-reducer-registry.ts、entity-collection-creator.ts、entity-cache-action.ts对应测试位于modules/data/spec/reducers/与 entity-data.module.spec.ts。赞分享前端状态管理【免费下载链接】platformReactive State for Angular项目地址https://gitcode.com/gh_mirrors/pl/platform点击查看免费下载相关推荐Redux Reducer 重构实战用函数式分解与 Reducer 组合拆解单体逻辑Redux Reducer 重构实战用函数式分解与 Reducer 组合拆解单体逻辑 本文以 Redux 官方文档 Refactoring Reducers前端Rematch config.redux 配置详解定制 Redux 的 initialState、reducer 合并、中间件与 DevtoolsRematch config.redux 配置详解定制 Redux 的 initialState、reducer 合并、中间件与 Devtools Remat前端Redux Reducer 结构组织完全指南从单一 reducer 到 reducer 组合与规范化状态Redux Reducer 结构组织完全指南从单一 reducer 到 reducer 组合与规范化状态 Redux 的核心是一个简单的设计模式所有写逻前端上一篇英雄联盟录像编辑终极指南免费开源工具League Director完全教程下一篇终极指南如何用RimSort彻底解决RimWorld模组冲突问题创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

相关推荐

自研CRM三个月实战复盘:从客户管理到外呼工作台的系统设计与落地
自研CRM三个月实战复盘:从客户管理到外呼工作台的系统设计与落地

DeskcommCRM听起来像个商业软件的名字,实际是我们团队从零自研并真正用起来的客户关系管理系统。这个项目从需求梳理到上线,前后大概三个月,最大的收获不是写了几万行代码,而是把销售、客服、外呼、数据看板这些原本散落在不同工具… · 2026/9/25 11:09:32

Oracle ERP外协加工全链路解析:从工单到发票的SQL对账与避坑指南
Oracle ERP外协加工全链路解析:从工单到发票的SQL对账与避坑指南

简介:这份文档面向制造业信息化从业者、Oracle ERP实施顾问及供应链管理人员,系统梳理Oracle Manufacturing模块中外协加工的完整业务逻辑与系统配置思路,帮助读者理解如何将供应商资源纳入自身制造流程,从而降低工程与制造成本、… · 2026/9/25 11:09:31

开源AI代码审查工具open-code-review实战指南:从合并请求到CI流水线
开源AI代码审查工具open-code-review实战指南:从合并请求到CI流水线

从团队里第一次尝试open-code-review到现在,我们组已经有四五个项目把它接进了日常的合并请求流程里。一开始我只当它是一个能自动挑刺的辅助工具,没想到用顺手以后,它成了我把关代码质量最省力的那一环。先说它到底是什么。open-code-review… · 2026/9/25 11:09:01

rkt 集成生态全景:容器编排、镜像分发、安全与监控的一体化对接指南
rkt 集成生态全景:容器编排、镜像分发、安全与监控的一体化对接指南

容器运行时云原生网络 【免费下载链接】rkt [Project ended] rkt is a pod-native container engine for Linux. It is composable, secure, and built on standards. 项目地址: https://gitcode.com/gh_mirrors/rk/rkt 点击查看 免费下载 rkt 是一个面向 Linux 的… · 2026/9/25 11:39:27

拆解MoE通信瓶颈:All-to-All、负载均衡与显存优化
拆解MoE通信瓶颈:All-to-All、负载均衡与显存优化

拆解MoE的通信瓶颈先交代一个背景:我前段时间训练一个8专家、64B参数级别的稀疏模型,跑了一周,MFU一直趴在35%上下。GPU利用率曲线倒是规律得很,冲高、跳水、冲高、跳水,隔一段时间就有一条明显的沟。最后把通信算子单… · 2026/9/25 11:39:21

Atlas 300V 24G推理加速卡上部署YOLO目标检测全流程解析
Atlas 300V 24G推理加速卡上部署YOLO目标检测全流程解析

收到一个挺有意思的提问。标题里孤零零一个“atlas”,后面跟着的两条热搜却把需求暴露得很完整:“atlas部署yolo”和“atlas 300v 24g 是运算加速卡吗”。两条搜索串起来,翻译成人话就是——手头有了一块Atlas加速卡,大概率是Atla… · 2026/9/25 11:39:21

高防IP防护链路拆解:从流量清洗到智能调度
高防IP防护链路拆解:从流量清洗到智能调度

很多人第一次接触高防IP,下意识会觉得这是一台“特别能扛打的大带宽服务器”。这个理解不算错,但只看到了结果,没看到过程。一个真正扛得住上百Gbps攻击的高防IP,背后其实是流量检测、流量牵引、清洗处置、回源转发、智能调度整套… · 2026/9/25 11:39:08

Themida/WinLicense脱壳实战:OEP定位与IAT修复工具链
Themida/WinLicense脱壳实战:OEP定位与IAT修复工具链

简介:该资源为 Themida/WinLicense V1.8.X-V2.X 的专用脱壳工具包,专注解决加壳软件在授权校验、反调试及代码虚拟化方面的保护问题,可辅助用户高效去除壳层并还原可用分析代码,主要面向软件逆向工程师、安全分析人员及有一定调试… · 2026/9/25 11:39:02

Photoshop改尺寸不糊指南:图像大小、画布大小、裁剪与导出全解析
Photoshop改尺寸不糊指南:图像大小、画布大小、裁剪与导出全解析

在修图这件事上,尺寸调整看似是最基础的操作,但我见过太多人栽在这一步。有人把手机拍的40003000照片直接拖进电商详情页模板,结果主体糊成一团;有人为了发朋友圈把图缩到800像素宽,回头想打印时发现原图已经覆盖保存&… · 2026/9/25 11:38:56

数值优化(Numerical Optimization)学习系列-03-共轭梯度方法(Conjugate Gradient)
数值优化(Numerical Optimization)学习系列-03-共轭梯度方法(Conjugate Gradient)

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/9/25 1:00:31

创维E900V22D刷机全攻略:S905L3SB芯片兼容性解析与救砖实战
创维E900V22D刷机全攻略:S905L3SB芯片兼容性解析与救砖实战

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/9/25 1:00:31

MQTT协议原理与Broker服务器搭建实战:从Mosquitto到EMQX
MQTT协议原理与Broker服务器搭建实战:从Mosquitto到EMQX

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/9/25 1:00:37

了解更多?预约专属演示

我们的顾问将为您一对一讲解产品与方案

企业微信二维码