Angular 4核心模块singleton中提供的服务是真的吗?

Angular 4核心模块singleton中提供的服务是真的吗?,angular,ng-modules,Angular,Ng Modules,我试图理解angular 4中的核心模块和单例服务。 官方文件()说明了以下内容: UserService是一个应用程序范围的单例。你不想要每一个 模块具有自己的独立实例。然而,确实存在一种危险 如果SharedModule提供UserService,则不会发生这种情况 CoreModule提供用户服务。Angular注册该提供程序 使用应用程序根注入器,创建 UserService可用于任何需要它的组件,无论 组件被急切地或惰性地加载 我们建议收集这样的一次性类并隐藏它们的 核心模块内的详细信

我试图理解angular 4中的核心模块和单例服务。 官方文件()说明了以下内容:

UserService是一个应用程序范围的单例。你不想要每一个 模块具有自己的独立实例。然而,确实存在一种危险 如果SharedModule提供UserService,则不会发生这种情况

CoreModule提供用户服务。Angular注册该提供程序 使用应用程序根注入器,创建 UserService可用于任何需要它的组件,无论 组件被急切地或惰性地加载

我们建议收集这样的一次性类并隐藏它们的 核心模块内的详细信息。简化的根AppModule导入 CoreModule作为应用程序的编排器作为 全部

所以我使用提供单例服务的核心模块和构造函数

constructor (@Optional() @SkipSelf() parentModule: CoreModule) { ... }
防止多次导入核心模块

1)但是,如果我在另一个模块中(例如在延迟加载模块中)提供UserService怎么办?此延迟加载的模块有一个新的服务实例?

关于forRoot方法:

@NgModule({
  imports:      [ CommonModule ],
  providers:    [ UserService ]
})
export class CoreModule {
}

static forRoot(config: UserServiceConfig): ModuleWithProviders {
  return {
    ngModule: CoreModule,
    providers: [
      {provide: UserServiceConfig, useValue: config }
    ]
  };
}
}
static forRoot(): ModuleWithProviders {
  return {
    ngModule: MyModule,
    providers: [
      MySingletonService
    ]
  };
2) 如果我在AppModule中使用CoreModule.forRoot()导入CoreModule,UserService会发生什么情况?也有吗

谢谢,是的。这与依赖关系注入器是分层的这一事实有关

这意味着,每个模块都有一组可以注入的元素(模块级),如果其中一个元素需要一个在模块级不存在的依赖项,那么依赖项注入程序将在模块的父模块(导入它的一个模块)中查找该依赖项,依此类推,直到找到依赖项或到达根目录(app.module),如果无法解析依赖项(层次结构级别),它将在根目录中抛出错误

2) 是,将提供
UserService
forRoot
将创建一个不同的
CoreModule
的“版本”,其中“普通”CoreModule用您添加的额外属性展开

在大多数情况下,
forRoot
将采用模块的“正常”版本,并包括提供者数组,以确保服务是单例的。模块的“正常”版本将仅包含组件、管道或其他非单音元素

以(提取的相关部分)的
TranslateModule
为例:


也许这个资源可以用作进一步的解释:

文档很混乱,尤其是这一行:

UserService是一个应用程序范围的单例。你不想要每一个 模块具有自己的独立实例但确实存在危险 如果SharedModule提供UserService,则不会发生这种情况

如果不使用延迟加载的模块,就不会发生这种情况。让我们看一个例子。您拥有导入
B
模块的
A
模块。两个模块都定义了提供程序:

@NgModule({
   providers: {provide: 'b', 'b'}
})
export class BModule {}

@NgModule({
   imports: [AModule]
   providers: {provide: 'a', 'a'}
})
export class AModule {}
@NgModule({
   providers: {provide: 'a', 'b'}
})
export class BModule {}

@NgModule({
   imports: [AModule]
   providers: {provide: 'a', 'a'}
})
export class AModule {}
编译器生成模块工厂时发生的情况是,它将这些提供程序合并在一起,并且只为一个模块创建工厂。下面是它的外观:

var AModuleNgFactory = jit_createNgModuleFactory0(

    // reference to the module class
    jit_AppModule1,  

    // array of bootstrap components
    [jit_AppComponent2],

    function (_l) {
        return jit_moduleDef3([

            // array of providers
            jit_moduleProvideDef4(256, 'b', 'b', []),
            jit_moduleProvideDef4(256, 'a', 'a', [])
            ...,
        ]);
您可以看到提供程序已合并。现在,如果您使用相同的提供程序令牌定义两个模块,则这些模块将被合并,并且来自导入另一个模块的提供程序将覆盖导入的模块提供程序:

@NgModule({
   providers: {provide: 'b', 'b'}
})
export class BModule {}

@NgModule({
   imports: [AModule]
   providers: {provide: 'a', 'a'}
})
export class AModule {}
@NgModule({
   providers: {provide: 'a', 'b'}
})
export class BModule {}

@NgModule({
   imports: [AModule]
   providers: {provide: 'a', 'a'}
})
export class AModule {}
工厂定义现在如下所示:

function (_l) {
    return jit_moduleDef3([

        // array of providers
        jit_moduleProvideDef4(256, 'a', 'a', []),
        ...,
    ]);
因此,无论导入多少模块,都只会创建一个具有合并提供程序的工厂。并且只创建了一个根注入器。部件产生的喷油器不是“真实”喷油器-检查以了解原因

这个延迟加载的模块有一个新的服务实例吗

对于延迟加载的模块,Angular会为它们生成独立的工厂。这意味着它们中定义的提供者不会合并到主模块注入器中。因此,如果延迟加载的模块使用相同的令牌定义提供者,Angular将创建该服务的新实例,即使主模块中已经有一个实例

如果我在AppModule中使用CoreModule.forRoot()导入CoreModule


要了解
forRoot
的作用,请参阅。

让我试着总结一下我认为学到的东西:

@NgModule({
  imports:      [ CommonModule ],
  providers:    [ 
    UserService,
    UserServiceConfig
    ]
})
export class CoreModule {
}

static forRoot(config: UserServiceConfig): ModuleWithProviders {
  return {
    ngModule: CoreModule,
    providers: [
      {provide: UserServiceConfig, useValue: config }
    ]
  };
}
}
当我使用forRoot方法导入模块时:

@NgModule({
  imports:      [ CommonModule ],
  providers:    [ UserService ]
})
export class CoreModule {
}

static forRoot(config: UserServiceConfig): ModuleWithProviders {
  return {
    ngModule: CoreModule,
    providers: [
      {provide: UserServiceConfig, useValue: config }
    ]
  };
}
}
static forRoot(): ModuleWithProviders {
  return {
    ngModule: MyModule,
    providers: [
      MySingletonService
    ]
  };
  • 还提供了UserService,因为(正如你们所解释的)forRoot创建了这个模块的扩展版本(它合并了服务)
  • UserServiceConfig是使用forRoot方法的config参数提供的
关于应用程序范围的单例:

为了拥有应用程序范围的单例服务(即使对于延迟加载的模块),我可以使用forRoot方法:

@NgModule({
  imports:      [ CommonModule ],
  providers:    [ UserService ]
})
export class CoreModule {
}

static forRoot(config: UserServiceConfig): ModuleWithProviders {
  return {
    ngModule: CoreModule,
    providers: [
      {provide: UserServiceConfig, useValue: config }
    ]
  };
}
}
static forRoot(): ModuleWithProviders {
  return {
    ngModule: MyModule,
    providers: [
      MySingletonService
    ]
  };
  • 在appModule中只能调用forRoot一次(那么为什么按照约定该方法被称为“forRoot”)
  • 如果在多个模块中导入该模块,则不会提供MySingletonService(因为它仅通过forRoot方法提供)
但是

如果我使用以下特殊构造函数创建CoreModule,它会阻止多次加载该模块,因此提供的服务是应用程序范围的单例服务:

constructor (@Optional() @SkipSelf() parentModule: CoreModule) {
  if (parentModule) {
    throw new Error(
      'CoreModule is already loaded. Import it in the AppModule only');
  }
}
所以在SharedModule中使用forRoot方法是有意义的,而不是在上面有特殊构造函数的模块中

因此,如果我想要应用程序范围(即使是惰性模块)的服务,我会看到两个选项:

  • 在appModule中调用forRoot方法的共享模块
  • 具有特殊构造函数的核心模块和正常提供的服务,没有forRoot方法
  • 有什么意见吗?

    谢谢,所以对于第1点,开发人员应该关心它,没有一种安全的100%的方式来创建一个