当心 Angular 中的单例 Service

你可能知道,当咱们经过@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也会释放那些用来储存数据的内存。工具

OnDestroy 钩子函数

许多开发者也许不知道非单例servicengOnDestroy()生命周期,因此你也能够在这个生命周期中进行一些销毁逻辑代码的编写,好比:ui

export class AdminService implements OnDestroy {
  ngOnDestroy() {
    // Clean subscriptions, intervals, etc
  }  
}

另外,若是咱们调用NgModuleRef.destroy()或者PlatformRef.destroy(),单例servicengOnDestroy钩子函数也会被[执行]。(https://github.com/angular/an...翻译

译者注

之因此翻译了这篇文章,是由于今天在整理项目代码的时候,偶然发现了这个问题,虽然我使用Angular也有一段时间了,可是依然将不少没有必要声明在NgModule中的服务以单例模式的方式声明了。文章中指出的问题确实是一个重要但又难以发现的问题。3d

大致总结一下Angular中声明service的不一样方式和应用场景。code

使用@Component

这时service与组件自己生命周期保持一致,非单例,适合声明一些须要暂存数据的工具类或者仅在某个或某几个组件中须要缓存数据的状态管理类service

使用@NgModuleproviders

这时service与应用自己生命周期保持一致(非懒加载),单例,适合声明一些须要在全局缓存数据的状态管理类service

可是有一个特例,懒加载模块中的service是会在模块加载时从新建立一个实例的,懒加载模块中均会注入后建立的service实例,所以懒加载模块与非懒加载模块间的service非单例。

使用forRoot

使用forRoot能够保证当前模块即便是懒加载模块,在加载时也不会从新建立一个新的service实例,由于懒加载模块在加载时,会临时建立一个从属于根injector的子injector,根据Angular中的依赖注入流程,当尝试经过一个子injector中注入不存在的实例对象时,会尝试向父级injector获取,所以最终可保证该service在应用任何地方被注入均是单例。

关于官方文档的介绍,能够参考ProvidersSingleton Services

相关文章
相关标签/搜索