【问题标题】:Angular Service not a global singletonAngular Service 不是全局单例
【发布时间】:2020-02-14 08:25:03
【问题描述】:

我的印象是 Angular ServiceSingleton,但最近发现,或者至少看起来,该服务只是组件实例和组件子级的单例。

考虑以下代码:

const CACHE_REFRESH_INTERVAL = 1800000; //30 minutes
const CACHE_SIZE = 1;

const COMPANY_CACHE_KEY = 'company_cache_key';

@Injectable({
  providedIn: "root"
})
export class CompanyService {

  private companyCache: Map<string, Observable<any>> = new Map();

  constructor(private httpClient: HttpClient) { }

  public getAllCompanies(): Observable<CompanyViewModel[]> {
    if (!this.companyCache[COMPANY_CACHE_KEY]) {
      const timer$ = timer(0, CACHE_REFRESH_INTERVAL);
      this.companyCache[COMPANY_CACHE_KEY] = timer$.pipe(
        switchMap(_ => this.getAllCompaniesHttpRequest()),
        shareReplay(CACHE_SIZE),
        refCount(),
        catchError(err => this.companyCache[COMPANY_CACHE_KEY] = undefined) //prevents storing error in cache
      );
    }
    return this.companyCache[COMPANY_CACHE_KEY];
  }

  private getAllCompaniesHttpRequest(): Observable<CompanyViewModel[]> {
    return this.httpClient.get<CompanyViewModel[]>(environment.endpoints.company.getAllCompanies());
  }
}

比方说,ComponentA 通过组件的指导员获取此服务。当ComponentA 调用CompanyService.getAllCompanies() 方法时,结果将存储在companyCache 中。现在用户导航到一个新的 URL,ComponentA 被销毁,以及服务的实例。导航回ComponentA,并注入一个新的服务实例,companyCache 再次为空。

我的假设正确吗?我可以在整个应用程序中将服务设为单例吗?

【问题讨论】:

  • 你的假设是正确的。我在两个应用程序中使用了这种模式的更简单版本。您如何获得as well as the instance of the service 的资格?你能设置一个堆栈闪电战来演示这个问题吗?
  • 只是补充一下,有趣的缓存模式。我在很大程度上已经从在我的应用程序中处理客户端缓存转移到使用电子标签,所以不必再担心这个了。

标签: angular typescript angular7


【解决方案1】:

服务是应用级别的单例。

参加这个简单的堆栈闪电战demo

我有一个简单的服务,它从构造函数中设置一个随机数。

@Injectable({
  providedIn: 'root'
})
export class Service {
  constructor() {
    this.rnd = Math.random();
  }

  private rnd: number;

  getRand(): number {
    return this.rnd;
  }
}

我有一个这样的组件结构,服务被注入 2 个“子”组件(不是实际的子组件,但这是我设置的导航模式):

|- home

|- component a
|--  inject service

|- component b
|--  inject service

从home导航到组件a时,会显示在服务的构造函数中设置的随机数。

当我导航回首页(检查控制台以查看组件 a 已被破坏),然后导航到组件 b 时,它将显示在服务的构造函数中设置的随机数。

在应用的生命周期内,随机数保持不变。对我来说,这证明即使服务不在“范围内”,它仍然在内存中。

更不用说the docs在第一行就谈到了这个:

单例服务是在应用中只存在一个实例的服务。

【讨论】:

  • @BradAxe mbojko 正在演示如何使用组件上的 providers 数组来覆盖单例服务。我已经分叉了我的例子来证明这一点:stackblitz.com/edit/angular-skdfwm。组件 A 继续使用单例,而组件 B 通过在本地提供程序中声明服务来使用本地实例。所以组件 A 将始终使用同一个实例,组件 B 将在每次访问时使用一个新实例。
【解决方案2】:

我的印象是 Angular 服务是单例,但最近发现,或者至少看起来,该服务只是组件实例和组件子级的单例。

当您将 CompanyService 放入组件的 providers 数组中时,就会发生这种情况。服务默认是单例的,但是你可以覆盖它。

拿一个小服务:

import { Injectable } from '@angular/core';

@Injectable({
  providedIn: 'root',
})
export class SomeService {
  public someRandomValue = Math.random();

  constructor() {
    console.log("I've just got instantiated.");
  }
}

还有两个在 providers 数组中包含 SomeService 的组件。对于他们俩来说,someRandomValue 会有所不同,并且“我刚刚被实例化了”。将被记录两次:

https://stackblitz.com/edit/angular-oua1nf?file=src%2Fapp%2Fsome.service.ts

【讨论】:

  • 但是从上面的代码可以看出,CompanyService设置为:providedIn: "root"
  • 尽管如此,它可以被覆盖。你有它在某处的providers 数组中吗?
  • 这对我来说似乎是合乎逻辑的解释。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-11-06
  • 2019-01-01
  • 2017-04-22
  • 1970-01-01
相关资源
最近更新 更多