NGRX/NX单仓Angular应用多模块复用公共状态库方案咨询
NGRX公共状态库多实例复用方案
1. 改造公共库核心逻辑
1.1 定义动态Feature Key注入令牌
将固定的Feature Key常量改为Angular注入令牌,支持不同模块传入自定义值:
// foo.feature.key.ts import { InjectionToken } from '@angular/core'; export const FOO_FEATURE_KEY = new InjectionToken<string>('FOO_FEATURE_KEY');
1.2 改造FooModule为可配置模块
通过forFeature静态方法接收自定义Feature Key,动态注册状态并提供依赖:
// foo.module.ts import { NgModule, ModuleWithProviders } from '@angular/core'; import { CommonModule } from '@angular/common'; import { StoreModule } from '@ngrx/store'; import { fooReducer } from './foo.reducer'; import { FOO_FEATURE_KEY } from './foo.feature.key'; import { FooService } from './foo.service'; @NgModule({ imports: [CommonModule], }) export class FooModule { static forFeature(featureKey: string): ModuleWithProviders<FooModule> { return { ngModule: FooModule, imports: [StoreModule.forFeature(featureKey, fooReducer)], providers: [ { provide: FOO_FEATURE_KEY, useValue: featureKey }, FooService ] } } }
1.3 改造选择器为工厂函数
不再导出固定选择器,改为接收Feature Key动态生成对应选择器的工厂方法:
// foo.selectors.ts import { createFeatureSelector, createSelector } from '@ngrx/store'; import { FooState } from './foo.state'; export function createFooSelectors(featureKey: string) { const selectFooState = createFeatureSelector<FooState>(featureKey); const selectSomeValue = createSelector(selectFooState, state => state.someValue); // 其他原有选择器全部在此处声明返回 return { selectFooState, selectSomeValue } }
1.4 改造Action为工厂生成(避免全局冲突)
NGRX的Action为全局广播,因此需要动态生成带唯一前缀的Action,避免不同实例互相干扰:
// foo.actions.ts import { createActionGroup, emptyProps, props } from '@ngrx/store'; export function createFooActions(featureKey: string) { return createActionGroup({ source: `[Foo ${featureKey}]`, events: { updateSomeValue: props<{ value: string }>(), resetState: emptyProps() // 其他原有Action在此处声明 } }) }
1.5 改造Facade服务动态绑定配置
在FooService中注入动态Feature Key,实例化对应选择器与Action,对外API保持不变:
// foo.service.ts import { Injectable, Inject } from '@angular/core'; import { Store } from '@ngrx/store'; import { FOO_FEATURE_KEY } from './foo.feature.key'; import { createFooSelectors } from './foo.selectors'; import { createFooActions } from './foo.actions'; @Injectable() export class FooService { private readonly selectors = createFooSelectors(this.featureKey); private readonly actions = createFooActions(this.featureKey); // 对外暴露的观察对象保持原有命名,业务侧无需修改 someValue$ = this.store.select(this.selectors.selectSomeValue); constructor( private store: Store, @Inject(FOO_FEATURE_KEY) private featureKey: string ) {} // 原有Facade方法保持原有签名,内部使用动态生成的Action updateSomeValue(value: string) { this.store.dispatch(this.actions.updateSomeValue({ value })); } reset() { this.store.dispatch(this.actions.resetState()); } }
2. 业务模块使用方式
不同业务模块导入FooModule时传入唯一的Feature Key即可获得独立状态实例:
// 第一个业务模块 import { NgModule } from '@angular/core'; import { FooModule } from '@your-org/foo-lib'; @NgModule({ imports: [FooModule.forFeature('firstBizFoo')] }) export class FirstBusinessModule {} // 第二个业务模块 @NgModule({ imports: [FooModule.forFeature('secondBizFoo')] }) export class SecondBusinessModule {}
3. 兼容localStorage同步需求
直接将所有自定义的Foo Feature Key加入localStorage同步配置即可:
import { ActionReducer } from '@ngrx/store'; import { localStorageSync } from 'ngrx-store-localstorage'; export function localStorageSyncReducer(reducer: ActionReducer<any>): ActionReducer<any> { return localStorageSync({ keys: ['firstBizFoo', 'secondBizFoo'], // 加入所有用到的Foo Feature Key rehydrate: true })(reducer); } // 根Store配置中加入meta reducer StoreModule.forRoot(rootReducers, { metaReducers: [localStorageSyncReducer] })
补充说明
- 所有通用逻辑全部保留在公共库内,无重复代码,维护成本不变
- 若需要兼容状态嵌套到业务模块State的场景,可扩展选择器工厂,接收上层状态选择器作为入参生成适配的选择器
- 每个模块的FooService为独立实例,完全隔离不同业务模块的状态操作
内容的提问来源于stack exchange,提问作者gorlock
相关产品推荐
相关产品推荐

