ngrx EffectsModule - 需要帮助理解提供者的使用情况

Wan*_*ker 3 ngrx-effects angular

我试图了解Angular中提供者工厂的使用.我理解Angular文档中提供的示例.

但是,我在ngrx EffectsModule(effects_module.ts)中遇到了一个相当不寻常的提供者工厂用法.

@NgModule({})
export class EffectsModule {
  static forFeature(featureEffects: Type<any>[]): ModuleWithProviders {
    return {
      ngModule: EffectsFeatureModule,
      providers: [
        featureEffects,
        {
          provide: FEATURE_EFFECTS,
          multi: true,
          deps: featureEffects,
          useFactory: createSourceInstances,
        },
      ],
    };
  }

  // *** <snip> forRoot method for brevity

}
export function createSourceInstances(...instances: any[]) {
  return instances;
}
Run Code Online (Sandbox Code Playgroud)

我理解的目的multi:true.

但是,我很难弄清楚为什么会featureEffects被声明为提供者,并且还被用作依赖性FEATURE_EFFECTS- 以及createSourceInstances(...instances: any[])实现了什么?

什么是这种模式,哪里可以使用它?

yur*_*zui 6

让我们从@ngrx/effects 文档开始.

我们需要做的第一件事是创建一个AuthEffects服务

@Injectable()
export class AuthEffects {
  @Effect()
  ...

  constructor(private http: HttpClient, private actions$: Actions) {}
}
Run Code Online (Sandbox Code Playgroud)

我们如何注册效果?

这很简单:

EffectsModule.forRoot([AuthEffects])
Run Code Online (Sandbox Code Playgroud)

要么

EffectsModule.forFeature([AuthEffects])
Run Code Online (Sandbox Code Playgroud)

现在,让我们停在这里思考是什么AuthEffects.

AuthEffects只是普通的角服务,可以有任何依赖关系(HttpClientActions在这种情况下),将与角DI系统的帮助下得到解决.

我们将这项服务传递给EffectsModule我们,我们也可以创造更多效果:

EffectsModule.forFeature([AuthEffects, MySecondEffects, ...])
Run Code Online (Sandbox Code Playgroud)

现在,让我们想象一下,我们是作家EffectsModule.

我们通过创建角度服务,让用户有机会提供任何效果.我们将在我们的库中使用这些服务:

addEffects(effectSourceInstance: any) {
    this.sources.addEffects(effectSourceInstance);
}
Run Code Online (Sandbox Code Playgroud)

正如您所看到的,我们需要提供我们提供的服务的实例,而不仅仅是类.

我们如何创建这些实例?

也许是这样的?

EffectsModule.forFeature([new AuthEffects(new HttpClient(...), new Actions(...))])
Run Code Online (Sandbox Code Playgroud)

当然不是!我们可以让角度DI来做到这一点:

EffectsModule.forFeature([AuthEffects])
...

forFeature(featureEffects: Type<any>[]) {
  ....
    providers: [
        featureEffects,
Run Code Online (Sandbox Code Playgroud)

现在,Angular DI知道所有这些服务以及如何创建它们.但我作为作者也希望在我的服务中使用它们,但我不知道如何得到它们......

@NgModule({})
export class EffectsFeatureModule {
  constructor(
    // i need to get all effects provided by users but they are hidden in DI system
    ...
  ) {

  }
}
Run Code Online (Sandbox Code Playgroud)

幸运的是,我们可以提供一个将在内部使用的令牌,并且将由库的作者知道.

为此,我们正在创建 InjectionToken:

export const FEATURE_EFFECTS = new InjectionToken<any[][]>(
  'ngrx/effects: Feature Effects'
);
Run Code Online (Sandbox Code Playgroud)

并定义FEATURE_EFFECTS令牌的配方:

  featureEffects,
  {
    provide: FEATURE_EFFECTS,
    multi: true,
    deps: featureEffects,
    useFactory: (...instances: any[]) => {
       return instances;
    },
  },
Run Code Online (Sandbox Code Playgroud)

multi告诉我们这个令牌可以多次定义(即我们有几个forFeatures调用)

deps指定该FEATURE_EFFECTS标记将使用将由Angular DI创建的所有featureEffects 实例.

useFactory 将这些实例作为参数.

通过这种方式,我们确切地知道注入FEATURE_EFFECTS令牌为我们提供了所有效果实例

让我们考虑是否可以省略featureEffects,例如:

{
  provide: FEATURE_EFFECTS,
  multi: true,
  useValue: featureEffects
},
Run Code Online (Sandbox Code Playgroud)

如果我们这样做,那么我们将不会获得实例,而是获取类(函数)的数组.但我们需要拥有所有实例化的类,只有Angular DI才是最好的朋友.

最后,虽然Angular DI是非常强大的模式,但它也有一些限制.

在AOT中,以下代码:

  featureEffects,
  {
    provide: FEATURE_EFFECTS,
    multi: true,
    deps: featureEffects,
    useFactory: (...instances: any[]) => {
       return instances;
    },
  },
Run Code Online (Sandbox Code Playgroud)

将导致错误:

"EffectsModule"中的装饰器不支持函数表达式考虑将函数表达式更改为导出函数.

所以这就是为什么它写成如下:

providers: [
  featureEffects,
  {
     provide: FEATURE_EFFECTS,
     multi: true,
     deps: featureEffects,
     useFactory: createSourceInstances,
   },
],
...
export function createSourceInstances(...instances: any[]) {
  return instances;
}
Run Code Online (Sandbox Code Playgroud)

我们可以在库中成功获取这些实例:

@NgModule({})
export class EffectsFeatureModule {
  constructor(
    ...
    @Inject(FEATURE_EFFECTS) effectSourceGroups: any[][],
...
Run Code Online (Sandbox Code Playgroud)