服务在不必要的情况下未被销毁(或刷新、清理...)

3

我有一个组件,其中注入了一个服务(我的数据存储库),可以通过延迟加载路由配置(loadChildren)访问该组件。该服务从其他http服务中提供数据。

当用户登出(到另一个不同的路由)并立即使用不同的用户登录时(一个人可以保留两个用户),存储服务会提供之前相同的数据(旧用户),而我认为它应该加载新数据(新用户)。

我认为在用户注销时应该“销毁”服务,因为服务观察者被销毁(在ngOnDestroy中),并且注入不是必要的,因此服务会重新构建。

我知道组件可以通过另一种方法从服务中获取新鲜数据,但我认为服务已经应该从Constructor-ngOnInt中获取数据。问题是该服务没有被销毁或清除...数据仍然来自旧用户。

这是我的代码。

组件

constructor(
public busquedasStore: BusquedasStore) { }

ngOnInit() {
 this.busquedasSubs = this.busquedasStore.busquedas.subscribe(b => {
   this.busquedas = b;
})

ngOnDestroy() {
 this.busquedasSubs.unsubscribe();
}

服务

import { Injectable } from "@angular/core";
import { Observable } from "rxjs/Observable";
import { BehaviorSubject } from "rxjs/BehaviorSubject";
import { UserService } from '../../core/user.service';

@Injectable()
export class BusquedasStore {

busquedas$: BehaviorSubject<{}[]> = new BehaviorSubject([]);

constructor(
 public userService:UserService
)  {
 this.cargarDatosIni();
}

cargarDatosIni() {
 this.userService.me().subscribe((res) => {
   let busquedas:{}[] = res.user.busqueda;
   this.busquedas$.next(busquedas);
 },err => {
   console.log(err);
 });
}

get busquedas(): Observable<{}[]> {
 return this.busquedas$;
}

1
服务是单例,它们的生命周期与组件不同。您有一个行为主题,它会将先前的值重放给新的订阅者,在这种情况下(直到您触发新的获取)是先前用户的数据。当用户登录以获取新数据时,应调用cargarDatosIni - jonrsharpe
2个回答

5

由于您代码中的项目名称是用西班牙语编写的,我没有更多上下文信息,但我可以理解您的意图。

您当前的实现

您的服务使用一个 BehaviorSubject 来“存储”用户登录后的有关当前用户的信息。服务在创建时自己检索该信息。在您登录时显示的组件中,您订阅该 BehaviorSubject 以获取该用户数据,然后在该组件的 ngDestroy 上取消订阅,因此您希望该服务也会被销毁,以便当您用其他人登录时,该服务将用该新用户的信息重新创建。

误解

不幸的是,您对 BehaviorSubject 的 subscribeunsubscribe 的理解存在误差,这也是您未获得预期行为的部分原因。BehaviorSubject 可以同时支持多个不同的订阅者,因此,一个订阅者取消订阅它将不会导致它停止产生值(或清除它已经生成的任何现有值)。

另一个误解是关于服务如何被创建和销毁的。如果一个服务是由 NgModule 提供的,它将只被创建一次,之后永远不会重新初始化。但是,如果您在组件级别提供服务,则该服务与提供它的组件共享生命周期,并且将在与组件同时创建和销毁。

解决方案

您有两个基本选项。最快实施的方法可能是仅在组件级别提供服务。因此,如果您在 NgModule 级别有一个提供程序配置用于您的服务,请删除它,并仅将其添加到您的组件中。

第二种选择,我认为可能是更好的长期选择,是更改您的服务,使其不在其构造函数中获取用户信息,而是让组件进行该调用。这样,每当新用户登录时,都会进行新调用以获取用户信息,而服务将始终反映当前用户的信息。

所以,

@Injectable()
export class BusquedasStore {

busquedas$: BehaviorSubject<{}[]> = new BehaviorSubject([]);

constructor(
 public userService:UserService
)  {
 //this.cargarDatosIni();  <-- remove this line
}

......

在您的组件中,
ngOnInit() {
 this.busquedasStore.cargarDatosIni();   // <--- add your service initialization call
 this.busquedasSubs = this.busquedasStore.busquedas.subscribe(b => {
   this.busquedas = b;
})

是的,我的当前解决方案是您的第二种解决方案。您所说的“第一个误解”在我的情况下不正确,我认为我理解了BehaviorSubjects,我知道这个可观察对象不会停止产生值...但是您的“第二个误解”是正确的,也是我疑惑的解决方案。谢谢。 - Javier RB

3

我不确定这是否能解决你的特定问题,但要注入的服务是由Injector从提供者层次结构中获取的,因此您可以在NgModule级别上注册提供者,也可以在Component级别上注册它。

@Component({
  // ...
  providers: [BusquedasStore]
  // ...
})
export class MyComponent {}

每次创建新组件时,都会收到一个新的服务实例(实际上,当组件被销毁时,它会销毁旧实例);如果您在NgModule中指定了该服务,它将覆盖当前的BusquedasStore服务。请参见https://angular.io/docs/ts/latest/guide/hierarchical-dependency-injection.html获取更多信息。

是的,这是正确的响应。我以为它们被销毁了,因为它们在模块级别被注入但没有被使用。 - Javier RB
@JavierRB 在模块级别,它们被注入到每个模块实例中(实际上与组件相同)。但是,由于每个应用程序只能有一个模块实例,因此该模块不会被销毁 => 服务也不会被销毁。 - smnbbrv

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接