【问题标题】:Why is my @Injectable service created more than once?为什么我的@Injectable 服务被创建了多次?
【发布时间】:2019-08-28 09:17:50
【问题描述】:

注意:请向下滚动到“更新”,因为问题归结为 @Injectable 服务被多次实例化。

我有一个加载业务的解析器:

resolve(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable<any> | Promise<any> | any {
  this._logger.debug('Loading application data.');
  return forkJoin([
    this._userService.getMe(),
    this._businessService.getAll()
  ]).pipe(
    tap(([user, businesses]) => {
      this._appState.user = user;
      this._appState.businesses = businesses;
    }),
    finalize(() => this._logger.debug('Application data loaded.'))
  );
}

有一个ApplicationState 有两个BehaviorSubject 成员_business_businesses

constructor() {
  this.businessChange = this._business.asObservable();
  this.businessesChange = this._businesses.asObservable();
}

set business(business: BusinessModel) {
  console.log(business);
  this._business.next(business);
}

get business(): BusinessModel {
  return this._business.getValue();
}

set businesses(value) {
  this._businesses.next(value);
  console.log(this.businesses);
}

get businesses(): Array<BusinessModel> {
  return this._businesses.getValue();
}

如您所见,上述状态的businesses 已在解析器中设置。 business 但是取决于路由并根据该路由设置在(延迟加载的)模块中:

ngOnInit() {

  this._appState.businessesChange.subscribe(
    (b) => {
      console.log('businessesChange');
      console.log(b);
    }
  );

  this._subscriptions.add(
    this._activatedRoute.params
      .subscribe((params) => {
        this._routeParams = params;
        const businessId: number = parseInt(params['businessId'], 10);
        const businesses = this._appState.businesses;
        console.log(`businessId ${businessId}`);
        console.log('available businesses');
        console.log(businesses);
        this._appState.business = businesses.find(b => b.id === businessId);
      })
  );
}

这是相关的日志输出:

2019-08-28T09:11:04.989Z DEBUG [..~df7ced28.js:428] Loading application data.
ngx-logger.js:251 2019-08-28T09:11:04.992Z DEBUG [main.js:398] Fetching /me
application.state.ts:56 Set businesses:
application.state.ts:58 (3) [{…}, {…}, {…}]
ngx-logger.js:251 2019-08-28T09:11:06.781Z DEBUG Application data loaded.
businesses.component.ts:43 businessesChange
businesses.component.ts:44 []
businesses.component.ts:47 businessId 4
businesses.component.ts:48 available businesses
businesses.component.ts:49 []
application.state.ts:46 Set business:
application.state.ts:47 undefined
ngx-logger.js:251 2019-08-28T09:11:08.386Z DEBUG Place is null. No action.

如您所见,businesses 被设置并包含三个元素。然后我们看到 应用程序数据已加载。 来自解析器。之后,我们看到_activatedRoute.params 订阅指出该路由显示了4businessId,但随后..this._appState.businesses 突然变成了一个空列表——BehaviorSubject 的初始值,尽管我们早点打印出来。

由于我也在记录/调试set 方法,我可以看到在设置为三个元素的列表后,该值不会发生变化。

我不知道这里发生了什么。什么可能导致这种情况?


我已经尝试过的:

  • 重启服务器
  • 使用调试器(但我还是 printf-debug)

更新

好的,我已将问题归结为ApplicationState 不止一次被实例化:

通过给ApplicationState 一个简单的ID(时间戳):

constructor() {
  this.businessChange = this._business.asObservable();
  this.businessesChange = this._businesses.asObservable();
  this.timestamp = moment().format('MM.SS SSS');
  console.log(`${this.timestamp} ApplicationState created`);
}

并在 setget 方法中更改日志输出,我明白了:

08.09 095 ApplicationState created
ngx-logger.js:251 2019-08-28T09:51:16.098Z DEBUG Loading application data.
ngx-logger.js:251 2019-08-28T09:51:16.099Z DEBUG [main.js:398] Fetching /me
:4200/#/businesses/4/calendar:1 This site does not have a valid SSL certificate! Without SSL, your site's and visitors' data is vulnerable to theft and tampering. Get a valid SSL certificate before releasing your website to the public.
application.state.ts:60 08.09 095 Set businesses:
application.state.ts:62 (3) [{…}, {…}, {…}]
application.state.ts:38 08.14 145 ApplicationState created
ngx-logger.js:251 2019-08-28T09:51:16.161Z DEBUG Application data loaded.
businesses.component.ts:43 businessesChange
businesses.component.ts:44 []
businesses.component.ts:54 businessId 4
businesses.component.ts:55 available businesses
businesses.component.ts:56 []
application.state.ts:50 08.14 145 Set business:
application.state.ts:51 undefined
ngx-logger.js:251 2019-08-28T09:51:16.178Z DEBUG Place is null. No action.
calendar.component.ts:105 

所以有一个08.09 095 (A) 和一个08.14 145 (B) 实例ApplicationState

A 设置了业务,但显然使用了进一步的 B。

我检查了,ApplicationState 在我的ApplicationModule 中只导入一次:

providers: [
  // Services
  ApplicationState,
  ApiServices,
  ApplicationResolver,
]

那么为什么会这样呢?这不应该是单例吗?是不是因为这超出了延迟加载的模块?

但最重要的是:为什么这在过去几周内奏效了?

我现在还添加了providedIn

@Injectable({providedIn: 'root'})
export class ApplicationState {

但它的行为仍然相同。

【问题讨论】:

  • 如果在取值的时候真的订阅了behavioursubjects,业务还是空的吗?
  • @JelleBruisten 刚试过.. 还是空的O_o
  • @JelleBruisten 我更新了日志输出和代码。 businessesChange_businesses 的 observable。
  • 似乎 behaviorSubject 值没有更新,即使在新订阅上,behavioursubject 也应始终输出最新值。因此看起来可能 applicationState 对象与以前使用的不一样?
  • @JelleBruisten 是的,我刚刚检查了这个(请参阅我的更新)。显然它被实例化了两次?这应该发生吗?我 thoguth 注射剂是单例的并且只被实例化一次?

标签: angular typescript


【解决方案1】:

好的,答案为什么会发生这种情况显然是由于延迟加载。它似乎与如何构建依赖关系树或类似的事情有关。

我不知道细节,我会接受任何关于这个主题更详细的分析器。

无论如何,我认为在 parent 模块中提供服务就足够了。因为它有意义,不是吗?

事实证明,这确实必须在app.module.ts 中提供。我有另一个模块,称为application.module.ts(不应混淆),我提供了ApplicationStateapplication.module.ts 是延迟加载的,但也有一些路由再次延迟加载,例如businesses.module.ts。所以我的层次结构看起来像这样:

app.module.ts
|_ application.module.ts  // lazy, providers: [ApplicationState]
|  |_ businesses.ts       // lazy, uses ApplicationState too
|_ public-pages.module.ts // not lazy

而我必须改变的是设置

providers: [ApplicationState]

app.module.ts

这不是理想的 imo,但由于延迟加载像这样工作,我将不得不处理它。

显然这是一个已知问题 (https://github.com/angular/angular/issues/12869),但堆栈上也有可能的解决方案overflowhttps://stackoverflow.com/a/41293784/826983

【讨论】:

    猜你喜欢
    • 2020-05-05
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多