我试图理解角度为4的核心模块和单例服务 . 官方文档(https://angular.io/guide/ngmodule)说明了以下内容:
UserService是一个应用程序范围的单例 . 您不希望每个模块都有自己独立的实例 . 然而,如果SharedModule提供UserService,则会发生这种情况 . CoreModule提供UserService . Angular通过app根注入器注册提供者,使得UserService的单例实例可用于任何需要它的组件,无论该组件是否被急切地或懒惰地加载 . 我们建议收集此类一次性类并将其详细信息隐藏在CoreModule中 . 简化的根AppModule作为整个应用程序的协调器导入CoreModule .
import { CommonModule } from '@angular/common';
import { TitleComponent } from './title.component';
import { UserService } from './user.service';
import { ModuleWithProviders, NgModule, Optional, SkipSelf } from '@angular/core';
@NgModule({
imports: [ CommonModule ],
declarations: [ TitleComponent ],
exports: [ TitleComponent ],
providers: [ UserService ]
})
export class CoreModule {
constructor (@Optional() @SkipSelf() parentModule: CoreModule) { ... }
}
所以我正在使用提供单例服务的核心模块和构造函数
constructor (@Optional() @SkipSelf() parentModule: CoreModule) { ... }
防止多次导入核心模块 .
1) BUT, what if I provide the UserService in another module (e.g in a lazy-loading module) ? This lazy-loaded module has a new instance of the service?
关于forRoot方法:
@NgModule({
imports: [ CommonModule ],
providers: [ UserService ]
})
export class CoreModule {
}
static forRoot(config: UserServiceConfig): ModuleWithProviders {
return {
ngModule: CoreModule,
providers: [
{provide: UserServiceConfig, useValue: config }
]
};
}
}
2)如果我在AppModule中使用CoreModule.forRoot()导入CoreModule,那么UserService会发生什么?它也提供了吗?
谢谢
3 回答
文档很混乱,尤其是这一行:
有's no danger of that happening if you don't使用延迟加载的模块 . 我们来看一个例子吧 . 您有导入
B
模块的A
模块 . 两个模块定义提供者:编译器生成模块工厂时会发生什么,这些提供程序将一起创建,并且将创建工厂 only for one module . 以下是它的外观:
您可以看到提供程序已合并 . 现在,如果使用 same provider token 定义两个模块,则将合并模块,并且导入另一个模块的提供程序将覆盖导入的模块提供程序:
工厂定义现在看起来像这样:
因此,无论您导入多少个模块,都只会创建一个具有合并提供程序的工厂 . 并且只创建了一个根注入器 . 组件创建的进样器不是"real"进样器 - 检查this answer以了解原因 .
对于延迟加载的模块,Angular会为它们生成 separate 工厂 . 这意味着它们中定义的提供程序不会合并到主模块注入器中 . 因此,如果延迟加载的模块使用相同的令牌定义提供者,Angular将创建该服务的新实例,即使主模块注入器中已有一个实例也是如此 .
要了解
forRoot
的作用,请参阅RouterModule.forRoot(ROUTES) vs RouterModule.forChild(ROUTES) .1)是的 . 这涉及依赖性注入器是 hierarchical 的事实 .
这意味着,每个模块都有一组可以注入的元素(模块级别),如果其中一个元素需要一个在模块级别不存在的依赖项,那么依赖注入器将在模块的父模块(导入它的模块),依此类推,直到找到依赖项或到达根目录(app.module),如果依赖项无法解析(层次结构级别),它将抛出错误 .
2)是的,将提供
UserService
.forRoot
将创建CoreModule
的 different ,其中"normal" CoreModule将使用您添加的额外属性进行扩展 .在大多数情况下,
forRoot
将采用模块的"normal"版本并包含providers数组,以确保服务是单例 . 模块的"normal"版本只包含组件,管道或其他非单体元素 .以ngx-translate的
TranslateModule
为例(提取相关部分):也许这个资源可以作为进一步的解释:https://www.youtube.com/watch?v=8VLYjt81-fE
让我试着总结一下我的想法:
当我使用forRoot方法导入模块时:
也提供了UserService(正如你们解释的那样)forRoot创建了这个模块的扩展版本(它合并了服务)
UserServiceConfig是使用forRoot方法的config参数提供的 .
And regarding application-wide singleton:
为了拥有应用程序范围的单例服务(即使是延迟加载的模块)我可以使用forRoot方法:
forRoot只能在appModule中调用一次(所以为什么按惯例调用方法为"forRoot")
如果模块是在多个模块中导入的,则不提供MySingletonService(因为它仅通过forRoot方法提供)
BUT
如果我使用以下特殊构造函数创建CoreModule,它会阻止模块多次加载,因此提供的服务是应用程序范围的单例:
因此,在SharedModule中使用forRoot方法是有意义的,而不是在具有上述特殊构造函数的模块中 .
因此,如果我想要应用程序范围(甚至是懒惰模块)服务,我会看到两个选项:
在appModule中调用了forRoot方法的共享模块
核心模块通常提供特殊构造函数和服务,没有forRoot方法
任何意见?