【问题标题】:Observable caching and cache clearing without invalidating subscriptions in Angular可观察的缓存和缓存清除,而不会使 Angular 中的订阅失效
【发布时间】:2019-12-06 07:58:30
【问题描述】:

我有两个组件都使用通过 http 从 API 端点检索到的数据。我想缓存来自 API 端点的响应,以便只发出一个 http 请求,但我也希望能够在我知道数据已更改时清除缓存,以便通过其原始订阅更新这两个组件。

我可以让缓存工作,因此使用publishReplay(1) 只发出一个http 请求。但是我找不到使用该方法使缓存无效的方法。

当我还没有数据的缓存版本时,我尝试创建自己的 observable 并调用 next(),但我不知道如何重新定义观察者函数,以便它在何时返回不同的数据我使缓存无效。

class ApiService {
  private user$: Observable<User>;
  ...
  getUser() {
    if (!this.user$)
      this.user$ = this.http.get<User>(`${apiUrl}users/me`).pipe(shareReplay(1));
    return this.user$;
  }

  invalidateUserCache() {
    // do what? Can't assign a new observable to this.user$ because 
    // any components that have already subscribed won't be notified
    // of the updates
  }
}

class UserProfileComponent implements OnInit {
  user: User;
  ...
  ngOnInit() {
    this.api.getUser().subscribe(user => this.user = user);
  }
  updateUser() {
    this.api.updateUser(someData).subscribe(response => {
      this.api.invalidateUserCache();
      // Since cache has been invalidated, I want this to 
      // make a new http request and notify other components 
      // that have subscribed.
      this.api.getUser().subscribe(user => this.user = user);
    }
  }
}

class MenuComponent implements OnInit {
  user: User;
  ...
  ngOnInit() {
    this.api.getUser().subscribe(user => this.user = user);
  }
}

上面的代码缓存了 User 对象,所以只发出了一个 http 请求,但是如果 UserProfileComponent 更新了用户,MenuComponent 不会被新用户更新。

【问题讨论】:

  • 您考虑过使用 BehaviorSubject 吗? Behaviorsubject 完全符合您的目标。

标签: angular caching observable


【解决方案1】:

如果要更新缓存,只需将引用值this.user$ 设置为 null。然后当再次执行getUser()方法时,它将运行一个新的服务器请求,然后更新之前存储的缓存。

 clearCache() {
    this.user$= null;
 }

【讨论】:

  • 这不会保留来自其他组件的订阅。它确实清除了调用 getUser() 的组件的缓存,但由于原始 observable 已丢失,因此无法更新其他组件。
【解决方案2】:

根据 Jeyenth 关于使用 BehaviorSubject 的建议,我想出了这个:

class ApiService {
  private _user: BehaviorSubject<User>;
  ...
  getUser() {
    if (!this._user) { // we haven't already requested the current user
      this._user = new BehaviorSubject(null);
      this.http.get<User>(`${apiUrl}users/me`).subscribe(user => this._user.next(user));
    }
    return this._user;
  }

  refreshUser() {
    this.http.get<User>(`${apiUrl}users/me`).subscribe(user => this._user.next(user));
  }
}

完美运行。谢谢杰恩斯!

【讨论】:

  • 使用 ReplaySubject 会更好吗?如果我们订阅 BehaviorSubject,我们从中得到的第一个值(null)是否必须被过滤?
猜你喜欢
  • 2019-05-21
  • 2019-01-28
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-07-10
  • 1970-01-01
  • 1970-01-01
  • 2017-05-24
相关资源
最近更新 更多