新闻详情

新闻详情

首页 / 资讯中心 / 详情

NgRx ESLint 规则 no-effects-in-providers 深入解析:Effect 类为何不能重复注册进 providers

发布时间:2026/9/26 10:15:45来源:尧图网络
NgRx ESLint 规则 no-effects-in-providers 深入解析:Effect 类为何不能重复注册进 providers
前端状态管理【免费下载链接】platformReactive State for Angular项目地址https://gitcode.com/gh_mirrors/pl/platform点击查看免费下载本文基于 NgRxAngular Reactive State 库本项目即其官方 platform 仓库自带的ngrx/eslint-plugin展开聚焦其中no-effects-in-providers这一条 ESLint 规则。该规则用于拦截“同一个 Effect 类既通过EffectsModule.forRoot()/forFeature()注册又出现在NgModule的providers数组中”的重复注册问题。读完本文你将掌握该规则的完整语义、自动修复行为、底层实现原理以及如何在项目中正确注册 NgRx Effects。规则概览no-effects-in-providers是ngrx/eslint-plugin中 effects 模块的一组规则之一其核心含义是Effectshould not be listed as a provider if it is added to theEffectsModule.即如果一个 Effect 类已经通过EffectsModule.forRoot()或EffectsModule.forFeature()注册就不应再把它写进 Angular 的providers数组。规则属性如下与文档头一致并由源码 no-effects-in-providers.ts 中的meta定义相互印证属性值含义Typeproblem属于“问题”类规则报告的是可能引发运行时异常的代码缺陷FixableYes支持 ESLint 自动修复--fix可安全移除冗余的 provider 声明SuggestionNo不提供手动修复建议suggestion只走自动修复通道Requires type checkingNo纯语法AST层面即可完成判断无需 TypeScript 类型信息ConfigurableNo不可配置schema: []无任何额外选项所属模块effects源码中docs.ngrxModule: effectsRule Details为什么 Effect 不能进 providers文档明确给出了规则的设计意图一个 Effect 类只应通过EffectsModule的forRoot()/forFeature()方法注册不应通过把 Effect 类加入 Angularproviders数组的方式注册。这一设计背后有清晰的源码依据。查看 effects_module.ts 可以发现EffectsModule.forRoot()与forFeature()在内部已经把传入的 effect 类注册为了 providersstatic forFeature(...featureEffects): ModuleWithProvidersEffectsFeatureModule { const effects featureEffects.flat(); const effectsClasses getClasses(effects); return { ngModule: EffectsFeatureModule, providers: [ effectsClasses, // ← 这里已经注册了 Effect 类 { provide: _FEATURE_EFFECTS, multi: true, useValue: effects }, { provide: USER_PROVIDED_EFFECTS, multi: true, useValue: [] }, { provide: _FEATURE_EFFECTS_INSTANCE_GROUPS, multi: true, useFactory: createEffectsInstances, deps: [_FEATURE_EFFECTS, USER_PROVIDED_EFFECTS], }, ], }; }forRoot()的实现也完全对称providers: [effectsClasses, ...]。也就是说通过EffectsModule.forRoot([CustomersEffect])/forFeature([CustomersEffect])传入的 Effect 类已经被 NgRx 作为 provider 注入并交由createEffectsInstances工厂统一创建实例、接入 Effects 运行链路如果开发者再次把同一个类放进NgModule的providers就会造成重复实例化同一种行为会被注册两次、副作用可能被重复订阅导致难以排查的运行时问题。从源码结构可以推断这正是规则将其归类为problem而非suggestion的原因——它拦截的是明确的反模式而非风格偏好。错误写法incorrect以下写法都是该规则要报告的目标。核心特征同一个 Effect 类同时出现在importsEffectsModule 调用与providers中。与 forRoot 搭配的错误示例NgModule({ imports: [EffectsModule.forRoot([CustomersEffect])], providers: [CustomersEffect], // ❌ no-effects-in-providers }) export class AppModule {}与 forFeature 搭配的错误示例NgModule({ imports: [EffectsModule.forFeature([CustomersEffect])], providers: [CustomersEffect], // ❌ no-effects-in-providers }) export class CustomersModule {}正确写法correct规则并不禁止 Effect 类出现在providers中——它只禁止已通过 EffectsModule 注册的类再次进入 providers。正确做法是把注册职责完全交给EffectsModule。与 forRoot 搭配的正确示例NgModule({ imports: [EffectsModule.forRoot([CustomersEffect])], }) export class AppModule {}与 forFeature 搭配的正确示例NgModule({ imports: [EffectsModule.forFeature([CustomersEffect])], }) export class CustomersModule {}需要补充说明的是未通过EffectsModule注册的 Effect 类放在providers里并不会触发此规则详见下文测试用例因为此时不存在重复注册问题。自动修复行为安全移除连逗号一起清理规则声明为Fixable: Yes其修复逻辑位于源码的fix回调中核心工具函数是 getNodeToCommaRemoveFixexport function getNodeToCommaRemoveFix(sourceCode, fixer, node) { const nextToken sourceCode.getTokenAfter(node); const isNextTokenComma nextToken ASTUtils.isCommaToken(nextToken); return [ fixer.remove(node), ...(isNextTokenComma ? [fixer.remove(nextToken)] : []), ]; }修复策略非常直观且安全移除被报告的 Effect 标识符节点本身检查它后面的下一个 token是否为逗号若是则一并移除从而保证providers数组的语法与格式依旧合法。因此对下面的代码执行eslint --fix或编辑器内“快速修复”NgModule({ imports: [EffectsModule.forFeature([RegisteredEffect])], providers: [RegisteredEffect] }) export class AppModule {}会被自动修复为NgModule({ imports: [EffectsModule.forFeature([RegisteredEffect])], providers: [] }) export class AppModule {}测试用例 no-effects-in-providers.spec.ts 中的第一条 invalid 用例即验证了该输出。同时由于修复只操作被报告的节点及其后的逗号与被移除元素相邻的注释会得到保留——例如行内注释// Lets see what happens with this comment?在修复后依然留在原位置见该 spec 中第二条用例块注释/* Deprecated effect */同样不被误删。规则如何工作基于 AST 选择器的实现原理规则的实现非常轻巧完全依赖typescript-eslint/utils提供的类型化 AST 与选择器机制。完整逻辑见 no-effects-in-providers.ts可拆解为三步第一步两条路径分别收集规则内部维护两个集合const effectsInProviders new SetTSESTree.Identifier(); const effectsInImports new Setstring();命中effectsInNgModuleProviders选择器时把providers数组里的每个标识符节点加入effectsInProviders命中effectsInNgModuleImports选择器时把EffectsModule.forRoot/forFeature([...])数组里的标识符名称加入effectsInImports。第二步选择器只匹配“Effect 注册调用”这两个选择器定义在 selectors/index.tsexport const ngModuleDecorator ClassDeclaration Decorator CallExpression[callee.nameNgModule]; export const ngModuleImports ${ngModuleDecorator} ObjectExpression ${metadataProperty(imports)} ArrayExpression; export const effectsInNgModuleImports ${ngModuleImports} CallExpression[callee.object.nameEffectsModule][callee.property.name/^for(Root|Feature)$/] ArrayExpression Identifier; export const effectsInNgModuleProviders ${ngModuleProviders} Identifier;要点在于effectsInNgModuleImports的正则/^for(Root|Feature)$/——它只匹配EffectsModule.forRoot(...)与EffectsModule.forFeature(...)这两种注册调用而StoreModule.forFeature(...)等其他模块方法会被自动排除不会干扰判断。这也是“Requires type checking: No”得以成立的原因全部判断都能在 AST 结构层面完成。第三步在 NgModule 装饰器退出时比对并报告[${ngModuleDecorator}:exit]() { for (const effectInProvider of effectsInProviders) { if (!effectsInImports.has(effectInProvider.name)) continue; context.report({ node: effectInProvider, messageId, fix: ... }); } effectsInImports.clear(); effectsInProviders.clear(); }规则选择在NgModule装饰器解析结束时:exit统一比对只有“既出现在 providers、又出现在 EffectsModule 注册列表”的标识符才会被报告。每次:exit后清空两个集合保证不同NgModule之间的状态互不污染。边界情况与测试验证测试文件 no-effects-in-providers.spec.ts 通过ruleTester覆盖了多条重要边界可作为理解规则语义的权威依据未注册的 Effect 放 providers 是合法的valid 用例providers: [FeatEffectTwo, UnRegisteredEffect, FeatEffectThree, RootEffectTwo]中没有任何一个出现在imports的EffectsModule.forRoot/forFeature调用里因此不报告。这与“规则只针对重复注册”的定位完全一致。多种 providers 写法均可识别测试覆盖了普通providers、计算属性形式[providers]、模板字符串形式[providers]选择器都能正确命中。多个 Effect 同时命中forRoot([RootEffectOne, RootEffectTwo])与多个forFeature([...])并存时凡重复出现在 providers 中的类如FeatEffectTwo、FeatEffectThree、RootEffectTwo会被逐条报告并修复未注册的UnRegisteredEffect则被保留。多模块隔离spec 中AppModule与SharedModule各自独立判断AppModule 的修复不影响 SharedModule反之亦然——这正是:exit后清空集合的设计收益。修复保持注释完整行内注释与块注释在自动修复后保留避免破坏开发者写的说明性注释。启用与使用方式该规则不可配置Configurable: Noschema: []属于 effects 模块规则。启用方式为在 ESLint 配置中通过ngrx/eslint-plugin的预设配置或直接引用规则名来开启例如在.eslintrc或eslint.config中声明ngrx/no-effects-in-providers。从源码结构看configs 目录按模块提供预设effects 相关规则会被统一聚合到该插件的 effects 配置集中。在本地开发时可以结合pnpm工作区运行该模块的测试以观察规则行为对应 spec 文件位于 spec/rules/effects。对存量代码直接对NgModule声明运行eslint --fix即可批量清理重复注册。进一步阅读EffectsModule 源码实现查看forRoot/forFeature内部如何注册 effect 类并创建实例规则源码查看meta、选择器监听与修复回调的完整实现规则测试用例覆盖错误/正确/修复/多模块等全部边界场景选择器工具定义理解effectsInNgModuleImports、effectsInNgModuleProviders等选择器的匹配规则Effects 使用指南了解 Effects 的标准注册与组织方式。赞分享前端状态管理【免费下载链接】platformReactive State for Angular项目地址https://gitcode.com/gh_mirrors/pl/platform点击查看免费下载相关推荐NgRx ESLint 插件 no-multiple-actions-in-effects 规则详解禁止 Effect 返回多个 ActionNgRx ESLint 插件 no multiple actions in effects 规则详解禁止 Effect 返回多个 Action 本文讲解 Ng前端状态管理NgRx Effects 规范no-dispatch-in-effects 规则详解禁止在 Effect 中直接调用 store.dispatchNgRx Effects 规范no dispatch in effects 规则详解禁止在 Effect 中直接调用 store.dispatch 导读前端状态管理NgRx no-multiple-global-stores 规则详解禁止在类中重复注入全局 StoreNgRx no multiple global stores 规则详解禁止在类中重复注入全局 Store no multiple global stores前端状态管理上一篇终极指南Deep-Live-Cam实时AI换脸实战3步实现影视级视频处理下一篇macOS文件预览终极增强QuickLook插件完全安装指南创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
网站建设高端定制企业官网
RELATED

相关资讯

更多精彩内容,欢迎继续阅读

较早相关资讯

最新相关资讯

今日AI:Fable 5 屠榜后,用 TaoToken 统一 Key 接入 Cline 的 config 骨架 2026/9/26 11:07:08

今日AI:Fable 5 屠榜后,用 TaoToken 统一 Key 接入 Cline 的 config 骨架

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

阅读更多 →
MyBatis 缓存双刃剑:一级缓存作用域、二级缓存失效与跨会话脏读生产避坑 2026/9/26 11:07:08

MyBatis 缓存双刃剑:一级缓存作用域、二级缓存失效与跨会话脏读生产避坑

MyBatis 缓存双刃剑:一级缓存作用域、二级缓存失效与跨会话脏读生产避坑 1. 先看一个真实困惑:改了数据库,接口为什么还返回旧值 假设现在有一个订单查询接口,逻辑很简单:根据订单号查订单,返回给前端。压测…

阅读更多 →
深入学Agent Harness工程(10):System Prompt运行时组装——用TaoToken统一Key打通配置链路 2026/9/26 11:07:08

深入学Agent Harness工程(10):System Prompt运行时组装——用TaoToken统一Key打通配置链路

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

阅读更多 →
Hermes 智能体完整安装教程:环境配置 + 依赖解决 + 验证测试(TaoToken 统一 Key 接入版) 2026/9/26 11:07:08

Hermes 智能体完整安装教程:环境配置 + 依赖解决 + 验证测试(TaoToken 统一 Key 接入版)

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

阅读更多 →
2025大模型技术全景:GPT、Claude、Gemini、文心一言等主流模型深度对比与选择(建议收藏) 2026/9/26 11:07:01

2025大模型技术全景:GPT、Claude、Gemini、文心一言等主流模型深度对比与选择(建议收藏)

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

阅读更多 →
定制瓦楞纸箱空箱抗压强度 (BCT) 全解析:定义、计算公式与核心影响参数 2026/9/26 11:07:01

定制瓦楞纸箱空箱抗压强度 (BCT) 全解析:定义、计算公式与核心影响参数

定制瓦楞纸箱空箱抗压强度 (BCT) 全解析:定义、计算公式与核心影响参数一、定义与核心概念空箱抗压强度(Box Compression Test,简称 BCT),指空纸箱在压力试验机上,以恒定速度沿竖直方向匀速施压&#xff0c…

阅读更多 →

今日资讯

本周资讯

本月资讯

看完文章仍有疑问?

联系尧图顾问,获取一对一建站咨询

立即免费咨询 📞 400-888-8888
📞 ✉