这是pluker。 https://plnkr.co/edit/tsNlmRth4mRzz0svGWLK?p=preview
我在其中创建了两个模块,每个模块包含两个组件和一个服务。 我希望服务应该是模块级别的单例(保存状态)。
如果单击“模块1页面1”和“模块2页面2”,它将显示两个不同的随机数。因为我在构造函数中生成此数字。因此,每次页面更改时都会创建服务。但是module2的表现非常完美。
注意:
Mode1Module正在使用loadChildren
Mode2Module正在使用children
我发现这个相同类型的问题已经按照此angular2 rc5 router service singleton
进行了修复但我认为它仍然存在。所以请帮我解决这个问题。
答案 0 :(得分:28)
延迟加载的模块有自己的根范围。添加到延迟加载模块的提供程序将在该根范围内获取实例,而不是应用程序的根范围。 如果将提供程序添加到未延迟加载的模块,则只会在应用程序根范围创建单个实例。
为什么延迟加载的模块中提供的服务仅对此可见 模块吗
与发布时加载的模块的提供商不同,提供商 延迟加载的模块是模块范围的。当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];
每次从任何其他模块的组件导航到任何Mod1Module的组件时都会创建Mod1Service,但是当您导航beetwen时,Mod1Module的组件服务将不会被重新创建。