你可能知道,当咱们经过@NgModule()
装饰器来声明一个service
时,它将符合单例模式,同时还意味着它与整个应用的生命周期保持一致。好比:git
export class AdminService { data = Array(10000).fill(dummy); } @NgModule({ providers: [AdminService, AdminDataService] })
咱们在刚开始接触Angular的时候,老是不计后果的将全部service
都使用@NgModule()
来声明,这将会形成一个不易发现的问题:github
You are not releasing memory.
在上面的例子中,尽管你再也不须要这些内存中储存的数据,可是让咱们停下来仔细想想,咱们真的须要将一个service
声明为单例的吗?缓存
好比,在咱们整个应用中,咱们会有一个管理区域须要呈现大量的表格数据(同时这些数据只在这个管理区域展示),这些数据会储存在内存中。在这种状况下,咱们没有必要将这个service
声明为单例的,由于咱们不须要缓冲层来缓存这些数据以供应用中的其余模块使用。ide
进一步讲,当前咱们仅仅是想使这些表格数据在多个component
之间共享,同时将数据与service
中的多个helper
方法耦合起来。因此咱们彻底能够直接使用@Component()
装饰器来声明service
,这样它就会成为一个非单例的service
,以下:函数
@Component({ selector: 'admin-tab', providers: [AdminService, AdminDataService] })
这样作的好处是,当Angular注销组件实例时,Angular将同时注销与之绑定的service
实例,y也会释放那些用来储存数据的内存。工具
许多开发者也许不知道非单例的service
有ngOnDestroy()
生命周期,因此你也能够在这个生命周期中进行一些销毁逻辑代码的编写,好比:ui
export class AdminService implements OnDestroy { ngOnDestroy() { // Clean subscriptions, intervals, etc } }
另外,若是咱们调用NgModuleRef.destroy()
或者PlatformRef.destroy()
,单例service
的ngOnDestroy
钩子函数也会被[执行]。(https://github.com/angular/an...。翻译
之因此翻译了这篇文章,是由于今天在整理项目代码的时候,偶然发现了这个问题,虽然我使用Angular
也有一段时间了,可是依然将不少没有必要声明在NgModule
中的服务以单例模式的方式声明了。文章中指出的问题确实是一个重要但又难以发现的问题。3d
大致总结一下Angular
中声明service
的不一样方式和应用场景。code
@Component
这时service
与组件自己生命周期保持一致,非单例,适合声明一些须要暂存数据的工具类或者仅在某个或某几个组件中须要缓存数据的状态管理类service
@NgModule
的providers
这时service
与应用自己生命周期保持一致(非懒加载),单例,适合声明一些须要在全局缓存数据的状态管理类service
。
可是有一个特例,懒加载模块中的service
是会在模块加载时从新建立一个实例的,懒加载模块中均会注入后建立的service
实例,所以懒加载模块与非懒加载模块间的service
非单例。
forRoot
使用forRoot
能够保证当前模块即便是懒加载模块,在加载时也不会从新建立一个新的service
实例,由于懒加载模块在加载时,会临时建立一个从属于根injector
的子injector
,根据Angular中的依赖注入流程,当尝试经过一个子injector
中注入不存在的实例对象时,会尝试向父级injector
获取,所以最终可保证该service
在应用任何地方被注入均是单例。
关于官方文档的介绍,能够参考Providers和Singleton Services。