对于使用loadChildren的angular2路由器延迟加载,服务不是单例

时间:2016-12-05 18:53:56

标签: angular angular2-routing

这是pluker。 https://plnkr.co/edit/tsNlmRth4mRzz0svGWLK?p=preview

我在其中创建了两个模块,每个模块包含两个组件和一个服务。 我希望服务应该是模块级别的单例(保存状态)。

如果单击“模块1页面1”和“模块2页面2”,它将显示两个不同的随机数。因为我在构造函数中生成此数字。因此,每次页面更改时都会创建服务。但是module2的表现非常完美。

注意: Mode1Module正在使用loadChildren Mode2Module正在使用children

我发现这个相同类型的问题已经按照此angular2 rc5 router service singleton

进行了修复

但我认为它仍然存在。所以请帮我解决这个问题。

3 个答案:

答案 0 :(得分:28)

延迟加载的模块有自己的根范围。添加到延迟加载模块的提供程序将在该根范围内获取实例,而不是应用程序的根范围。 如果将提供程序添加到未延迟加载的模块,则只会在应用程序根范围创建单个实例。

https://angular.io/docs/ts/latest/cookbook/ngmodule-faq.html#!#q-lazy-loaded-module-provider-visibility

  

为什么延迟加载的模块中提供的服务仅对此可见   模块吗
  与发布时加载的模块的提供商不同,提供商   延迟加载的模块是模块范围的。

     

当Angular路由器延迟加载模块时,它会创建一个新模块   执行上下文。该背景有自己的注射器是直接的   应用程序注入器的子项。

     

路由器添加了懒惰模块的提供者及其提供者   将模块导入此子进样器。

     

这些提供商不受应用程序提供商变更的影响   使用相同的查找标记。当路由器在其中创建组件时   在延迟加载的上下文中,Angular更喜欢创建的服务实例   从这些提供程序到应用程序根目录的服务实例   喷射器。

https://angular.io/docs/ts/latest/cookbook/ngmodule-faq.html#!#q-why-bad

  

如果SharedModule为延迟加载提供服务,为什么不好呢?   模块吗
  我们在Angular Module章节中提出了这个问题   讨论了使提供者远离SharedModule的重要性。

     

假设我们在模块的提供商中列出了UserService(其中   我们没有)。假设每个模块都导入这个SharedModule(哪个   他们都这样做。)

     

当应用程序启动时,Angular急切地加载AppModule和   ContactModule。

     

导入的SharedModule的两个实例都会提供   UserService。 Angular在根应用程序注入器中注册其中一个   (往上看)。然后一些组件注入UserService,Angular找到它   在应用程序根注入器中,并提供应用程序范围的单例   UserService。没问题。

     

现在考虑延迟加载的HeroModule!

     

当路由器延迟加载HeroModule时,它会创建一个子注入器   并使用该子注入器注册UserService提供程序。该   子进样器不是根进样器。

     

当Angular创建一个懒惰的HeroComponent时,它必须注入一个   UserService。这次它在懒惰中找到了一个UserService提供者   模块的子注入器并创建UserService的新实例。   这是一个与应用程序范围完全不同的UserService实例   单人版本,Angular注入其中一个急切负载   组件。

     

这几乎肯定是一个错误。

     
    

为自己证明。运行实例。修改SharedModule     这样它就提供了UserService而不是CoreModule。然后     在"联系人"之间切换和#34;英雄"链接几次。该     当Angular创建一个新的UserService时,用户名就会疯狂     每次都是实例。

  

https://angular.io/docs/ts/latest/cookbook/ngmodule-faq.html#!#q-why-child-injector

  

为什么延迟加载会创建子注入器?
   Angular补充道   @NgModule.providers到应用程序根注入器...除非   模块是延迟加载的。然后它创建一个子注入器并添加   模块的儿童注射器供应商。

     

这意味着模块的行为取决于它是否有所不同   在应用程序启动期间加载或稍后延迟加载。忽视   这种差异可能导致不良后果。

     

为什么没有Angular将延迟加载的提供程序添加到app根注入器   正如它为急切加载的模块所做的那样?为什么不一致?

     

答案基于Angular的基本特征   依赖注入系统。注入器可以添加提供程序直到它   第一次使用。一旦注射器开始创建和提供服务,   其提供者列表已冻结。不允许新的提供商。

     

当应用程序启动时,Angular首先配置根目录   注入器与所有急切加载的模块的提供者之前   创建其第一个组件并注入任何提供的组件   服务。应用程序开始后,应用程序根注入器将关闭   新的供应商。

     

时间过去了。应用程序逻辑触发模块的延迟加载。   Angular必须将延迟加载的模块提供程序添加到注入器   某处。它不能将它们添加到app根注入器中,因为那样   注射器对新的供应商是封闭的。所以Angular创造了一个新的孩子   延迟加载模块上下文的注入器。

答案 1 :(得分:19)

正如@GünterZöchbauer所提到的,这是Angular文档的解释,其中延迟加载的模块获得自己的服务实例。如果服务是真正的单身人士,那么你应该

直接将其提供给AppModule

@NgModule({
  providers: [ SingletonService ]
})
class AppModule {}

这些服务是应用程序范围的,甚至是延迟加载的模块。因此,您无需将其添加到那些延迟加载的模块的providers中。

将提供服务的模块导入AppModule

请注意,约定是使用forRoot,如[docs] [1]

中所述
@NgModule({
})
class CoreModule {
  forRoot() {
    return {
      ngModule: SomeModule,
      providers: [SingletonService]
    }
  }
}

@NgModule({
  imports: [ CoreModule.forRoot() ]
})
class AppModule {}

forRoot只应在AppModule导入中调用。这将确保只有AppModule添加提供者。任何延迟加载的模块都可以访问AppModule提供的相同单件服务。

答案 2 :(得分:2)

您可以通过将Mod1Page1和Mod1Page2组件作为子包装在一个Mod1组件中来解决此问题:

@Component({
  template: '<router-outlet></router-outlet>'
})
class Mod1Component {}

const routes: Routes = [
  {
    path: '',
    component:Mod1Component,
    children:[
      {
        path : 'page1',
        component : Mod1Page1Component
      },
      {
        path : 'page2',
        component : Mod1Page2Component
      }
    ]
  }
];

const routedComponents = [Mod1Component,Mod1Page1Component, Mod1Page2Component];

Plnkr link to solved fork

每次从任何其他模块的组件导航到任何Mod1Module的组件时都会创建Mod1Service,但是当您导航beetwen时,Mod1Module的组件服务将不会被重新创建。