搜索

前端如何更好的实现接口的缓存和更新

原创 2016-11-01 14:23:01 1223
摘要:以传统方式来说,我们一般会搞一个store,上层业务的请求都是从它走,由它去拿到远程数据,并且控制缓存。但我们面临一个问题,当数据更新了,需要通知所有使用这个数据的业务方,所以就不得不在这里再补一种机制,用于通知上层。通常就是某种订阅机制:store1.subcribe("somedata", data => {   //

以传统方式来说,我们一般会搞一个store,上层业务的请求都是从它走,由它去拿到远程数据,并且控制缓存。


但我们面临一个问题,当数据更新了,需要通知所有使用这个数据的业务方,所以就不得不在这里再补一种机制,用于通知上层。通常就是某种订阅机制:

store1.subcribe("somedata", data => {
  //处理这个data,更新到视图模型上供刷新界面
  viewModel.somedata = data;
});


这样问题是解决了,但业务代码就比较繁琐了,为什么我还要在每个地方都写订阅?尤其是如果上层用了angular或者vue,本来可以很简单的,你这一弄,框架刚帮我简化了一些代码,又被搞得很长了。

因为之前我们第一次查询数据,不是这么干的,而是:

service1.getData().then(data => {
  //处理这个data,更新到视图模型上供刷新界面
  viewModel.somedata = data;
});


所以这里就出现了不同的两种方式来更新界面数据,这非常讨厌。

我之前提出过一种折中的解决办法,比如在angular中,可以这样:

- store中保存数据对象的引用,上层业务在查询的时候,持有这个引用,并且,不得修改内容
- 如果需要修改或者更新远程数据,调用store中封装的修改方法,把数据合并到数据对象中

这样,界面上的刷新之类,可以不劳自己费心,但整体还是挺别扭的,而且,限制上层不能动引用,这个事情很坑,万一需要二次加工数据,就歇菜了。

React有一套机制来处理这个事情,留给这派的人来阐述吧。

我说说另外一种方式,FRP。

在egghead上有一个RxJS的视频系列,其中有一节大致介绍了使用Reactive理念处理这种场景,我觉得非常好,地址如下,可能要会员才能看:

Reactive Programming

它这个场景就是为了复用请求,里面的代码摘录:

var startupRequestStream = Rx.Observable.just('https://api.github.com/users');var requestOnRefreshStream = refreshClickStream
  .map(ev => {
    var randomOffset = Math.floor(Math.random()*500);
    return 'https://api.github.com/users?since=' + randomOffset;
  });var requestStream = startupRequestStream.merge(requestOnRefreshStream);var responseStream = requestStream
  .flatMap(requestUrl =>
    Rx.Observable.fromPromise(jQuery.getJSON(requestUrl))
  )
  .shareReplay(1);
// refreshClickStream: -------f------------->
// requestStream:      r------r------------->
// responseStream:     ---R-------R--------->
// closeClickStream:   ---------------x----->
// suggestion1Stream:  N--u---N---u---u----->
function getRandomUser(listUsers) {
  return listUsers[Math.floor(Math.random()*listUsers.length)];}function createSuggestionStream(responseStream, closeClickStream) {
  return responseStream.map(getRandomUser)
    .startWith(null)
    .merge(refreshClickStream.map(ev => null))
    .merge(
      closeClickStream.withLatestFrom(responseStream, 
                                  (x, R) => getRandomUser(R))
    );}

这里面的关键在于:shareReplay和withLatestFrom。

shareReplay的介绍在这:shareReplay | RxJS

withLatestFrom的介绍在这:withLatestFrom

抛开代码细节不谈,我觉得理念是这样:

我把请求当做流,然后,上层业务来订阅这个流,正常来说,我们的期望可能是,订阅之后,流的每次更新都会得到处理,但这时可能出现:

某一个订阅者来得太晚,流已经走了一段数据了,会需要“补课”。

所以,Rx的机制会允许我们缓存一些数据,供“我们来晚了”的朋友们使用。

使用这种流的理念来处理,有什么好处呢?那就是可以保证上层业务的一致性。对于上层代码,我只管是从这个流获取数据,更新界面,我不关注你是哪来的,只认你一家,反正只要根据你那边的信息,都作了对应处理,就肯定好了。

而这个流的提供方,也很方便,他只需考虑把不同来源的数据进行归并,从多个流合并成一个流,就是这么简单。

Angular 2深度整合了RxJS,可以关注,不过RxJS本身是不绑定到任何框架的,你跟jQuery或者React配合使用,也是没问题。


发布手记

热门词条