@ ngrx / Store如何用于缓存?

时间:2017-08-22 10:17:27

标签: ngrx

ngrx声明(here for example)的支持者,您可以并且应该将所有应用程序状态保存在单个商店中。这表明@ ngrx / Store可以用于缓存,因为缓存的内容是一种应用程序状态。

Web应用程序中的缓存是一种在它拥有数据时返回数据的东西,并且当它没有时封装从服务器请求数据。 Wikipedia article on caching在数据可用时将其称为缓存命中,而在数据不可用时将其称为缓存未命中。

从功能编程的角度来看,我们可以立即看到从缓存中读取数据在功能上是不纯的 - 它有副作用,即可以从服务器请求数据并保留在缓存中。我不知道如何用ngrx做到这一点,例如,要求其选择器在功能上是纯粹的。

考虑这个Caching With RxJs Observables in Angular教程可能有所帮助(据说rxjs与ngrx极为互补)。我们没有必要向远处滚动才能找到带有副作用的getFriends()函数:

getFriends() {

  if(!this._friends){

    this._friends = this._http.get('./components/rxjs-caching/friends.json')
                        .map((res:Response) => res.json().friends)
                        .publishReplay(1)
                        .refCount();
  }

  return this._friends;
}

此外,Store的内容似乎普遍适用于整个应用程序。唯一的控制是关于如何更新状态,但是在缓存的未处理的原始数据中徘徊是愚蠢的,因为无法保证哪些缓存项可用且哪些不可用。

希望这些担忧能够得到缓解,并且我已经错过了这样做的方法。请问您能否将@ ngrx / Store用作缓存?

4 个答案:

答案 0 :(得分:1)

在ngrx中有减速器(纯函数)可以改变状态(或缓存)。这些减速器由您在商店发送的动作触发。

从商店中,您可以通过选择器请求数据切片并订阅他们的更改。

要实现缓存逻辑,请检查数据是否可用,如果不可用 发送一个像“LoadDataSliceAction”这样的动作,它会触发一个副作用然后将数据加载到商店中。

这有帮助吗?

答案 1 :(得分:0)

我发现了两种主要方法。它们都涉及为商店的任何需要过期的部分添加时间戳。第一种是检查选择器功能中的年龄,如果超过了限制,则只需分派检索操作。使用createSelector语法并可以访问商店进行分发,这一点相当简单。另一个要求您按正常方式调度检索操作,然后在检索之前检查效果中的数据寿命。这可以通过将可观察到的sttore与combinLatest(https://medium.com/@viestursv/how-to-get-store-state-in-ngrx-effect-fab9e9c8f087)之类的东西组合来实现。

第一个选项示例

createSelector(previousSelector, (item) => {
    const now = new Date();
    const limit = new Date(now.getTime() - (5 * 60000));
    if (item.age < limit) {
        this.store.dispatch(new getFreshDataAction());
    }
});

答案 2 :(得分:0)

值得注意的是,浏览器标配缓存,这是真正的交易,封装请求和封装数据,因为它不具有问题中讨论的nrgx的局限性。

这些天,我们正在使用浏览器缓存来存储实体。从here借用我的定义:

  

实体是一个具有长期数据值的对象,您可以从服务器读取该数据(并有可能写回该数据)。

     

实体数据只是一种应用程序数据。配置数据,用户角色,用户的先前选择,屏幕布局的当前状态…这些都是重要的数据,可以从使用标准ngrx技术进行编码中受益,但它们不是实体。

这会篡改Redux的基本原理之一(ngrx是Redux的实现),即ngrx存储是整个应用程序的single source of truth。在这里,我们说的是不同的话。这里我们说的是浏览器缓存是我们实体的唯一事实来源,而ngrx Store是其他所有事物的唯一事实来源(实际上比这还差一点:如果一个实体是可编辑的,那么Store必须在编辑时对此负责。

基础很简单。我们在实体的http响应上设置版本号和缓存时间:

X-Version: 23
Cache-Control: max-age=86400

当我们收到http响应时,我们将像以前一样处理它的主体,但是不会将其存储在任何地方。我们将其留给浏览器来为我们完成,如果我们第二次需要数据,我们只是简单地重新请求它,并且由于浏览器从其缓存中提供数据,因此数据几乎是即时到达的。

如果我们看到的实体版本已过时,则我们立即重新请求它,并确保通过在请求标头中设置Cache-Control: no-cache来覆盖缓存。这样可以确保我们始终使用实体的当前版本。

        this.http.get<T>(
          url,
          {...options, headers: {'Cache-Control': 'no-cache'}}
        )

但是我们怎么知道一个版本已经过时了?幸运的是,我们的版本控制系统非常精细,因此我们不会经常获得更新。通过与我无关的Web套接字来更新当前版本号。因此请记住,我们是有福的,这种方法可能对您不起作用(至少在没有经过认真思考的情况下),并且计算机科学中只有two hard things:缓存无效并命名事物;-)

还需要保持警惕,因为我知道有几种方法可能会意外禁用浏览器缓存,这显然很可能会导致性能下降:

  • 在调试器内部,例如Chrome DevTools中有一个“禁用缓存”复选框。
  • 错误的SSL证书。如果浏览器不信任服务器,那么不保留接收到的数据是正确的。这只是在试图欺骗并通过单击“高级”和“继续”来响应“您的连接不是私有的”的测试环境中的问题。

那么,如何解决这个问题(这不会让所有人高兴)? Ngrx 不能用作真正的缓存,但是浏览器可以!

答案 3 :(得分:0)

您可以执行类似的操作

friends$ = createEffect (() =>{
  this.actions$.pipe(
   .ofType(ListActions.LOAD)
   .withLatestFrom(
     this.store.select(fromSelectors.loadedList),    
     (action: fromList.Load, store: fromList.State) => store.friends
   )
  .switchMap((friends: Friend[]) => {

    if (friends) {
      return [new ListActions.Loaded(friends)];
    }

    return this.http
      .get('/friendApi')
      .map((friends: Friend[]) => new ListActions.Loaded(friends))
      .catch(err => Observable.of(new ListActions.Failed()));
   });
}

使用“ withLatestFrom”运算符,我们通过分派的动作使存储(已加载列表)状态生效,并检查列表是否被填充;调度现有列表,否则进行其余调用并更新我们在reducer中的商店。 有关详细答案,请参见this Medium post