使用 EmberData 在本地存储中缓存远程数据

Posted

技术标签:

【中文标题】使用 EmberData 在本地存储中缓存远程数据【英文标题】:Caching remote data in Local Storage with EmberData 【发布时间】:2013-01-26 16:36:55 【问题描述】:

我有一个关于使用 Ember 加载和缓存远程对象的问题。我正在开发一个通过 REST API 使用服务器端存储的 Ember 应用程序。某些获取的数据很少更改,因此每次应用程序加载时都从服务器获取数据是不必要的。但这对于需要离线工作并仍将其数据保存到服务器的应用程序来说也是一个问题。

Ember Data 有一个内置的存储适配器,用于通过 REST API 持久化模型,还有an adapter for Local Storage(正如下面的 Ken 指出的那样)。问题(如果是问题的话)是一个模型只有一个存储适配器,除了将它们保存在内存中之外,似乎没有任何缓存获取的模型的概念。

我在 Ember wishlist 和 talk by Tom Dale 的 cmets 中发现了类似的请求,但我没有发现任何迹象表明这将是 Ember 中的现有功能。

我有两个问题(第一个很重要):

    今天,在本地存储中实现缓存模型并根据需要将它们与远程数据同步的最佳方式是什么? 这是计划包含在 Ember 中的功能,还是至少维护者认为最终应该添加的功能?

谈到 1),我可以想到几个策略:

a) 扩展现有适配器并添加自定义远程同步机制:

App.Store.registerAdapter('App.Post', DS.LSAdapter.extend(
  // do stuff when stuff happens
));

b) 维护单独的模型类——一组用于远程对象,一组用于本地对象——并根据需要在它们之间进行同步。使用标准 Todo 案例:

RemoteTodo –*sync*– Todo
                     |
                     UI

我有点希望这是一个真正的菜鸟问题,并且有一个很好的既定模式。

更新:找到this similar question。它有一个很好的答案,但它是一种理论上的。我想我需要的是一些实用技巧或示例实现的指针。

【问题讨论】:

【参考方案1】:

有一个本地存储适配器的实现,您可能会发现它很有用。看看https://github.com/rpflorence/ember-localstorage-adapter

【讨论】:

感谢您的指点——我实际上认为适配器是 Ember Data 的一部分。我会相应地更新我的问题。不过,它并没有真正解决我的问题,因为我正在寻找一种使用带有 LS 缓存的 REST 后端的好方法——不要将我的数据专门存储在 LS 中。 您可以在同一个应用程序中使用不同的适配器,您可以将您选择的适配器分配给特定的模型。 是的。这就是您在我的问题中实施 b) 的方式。不过,我仍然想知道最好的方法。 @hannes_l 想知道你是否解决过这个问题?我有完全相同的用例,其中我的事务和参考数据都由同一个 REST 服务器支持,但我想将参考数据缓存在 localStorage 中。我在想我可以使用 Ryan 的 localStorage,但可以扩展它以进行 Ajax 刷新调用(用于提取数据)并拦截写入(对我来说非常不常见)并更新 localStorage 和 REST 后端。如果存在某些东西,宁愿重复使用。 @ken 对不起,不。我最终没有在我的项目中使用本地存储。【参考方案2】:

只是为了“提升”这个线程一点,因为当我研究restful api的ember本地缓存等解决方案时,它是最好的结果之一:

Dan Gebhardt 似乎在 Orbit.js 及其与 Ember 的集成方面做得非常好: https://github.com/orbitjs/ember-orbit

Orbit 是一个独立的库,用于协调对数据源的访问 并保持内容同步

Orbit 为构建高级功能奠定了基础 离线运维等客户端应用 本地缓存、撤消/重做堆栈和临时编辑的同步 上下文。

Orbit.js 功能:

在一个应用程序中支持任意数量的不同数据源,并通过通用接口提供对它们的访问。

允许满足不同来源的请求,包括指定优先级和后备计划的能力。

允许记录同时存在于不同来源的不同状态。

跨源坐标转换。尽可能自动处理合并,但允许完全自定义控制。

允许阻塞和非阻塞转换。

允许同步和异步请求。

通过跟踪操作的逆向来支持事务和撤消/重做。

使用纯 javascript 对象。

不要错过他关于 Orbit 的精彩演讲和幻灯片:Introduction to Orbit.js

更新:我从 Orbit 页面添加了一些更多描述性信息,因为我的帖子因“仅”引用外部资源而不包含实际解决方案本身而被否决。但 Orbit 在我看来就像解决方案一样,在这里“包含”它的唯一方法是通过链接。)

【讨论】:

虽然这些链接可能会回答问题,但最好在此处包含答案的基本部分并提供链接以供参考。如果链接页面发生更改,仅链接的答案可能会失效。【参考方案3】:

这是一种方法。适配器的 mixin,使用方法 localStoreRecord 可用于缓存记录,最后是用于预加载存储的初始化程序。

本地存储只是字符串化对象的键值对存储,因此我们可以将所有应用程序数据存储在一个键下。

注意:这是使用 es6 模块

// app/mixins/local-storage.js

import Ember from 'ember';

export default Ember.Mixin.create(
  appName: 'myApp',
  // how many records per model to store locally, can be improved.
  // needed to prevent going over localStorage's 5mb limit
  localStorageLimit: 5,
  localStoreRecord: function(record) 
    var data = JSON.parse(localStorage.getItem(this.appName));
    data = data || ;
    data[this.modelName] = data[this.modelName] || [];
    var isNew = data[this.modelName].every(function(rec) 
      rec.id !== record.id; 
    );
    if (isNew) 
      data[this.modelName].push(record);
      if (data[this.modelName].length > this.localStorageLimit) 
        data[this.modelName].shift();
      
      localStorage.setItem(this.appName, JSON.stringify(data));
    
  
);
// app/adapters/skateboard.js

import DS from 'ember-data';
import Ember from 'ember';
import LocalStorageMixin from '../mixins/local-storage';

export default DS.RESTAdapter.extend(LocalStorageMixin, 
  modelName: 'skateboard',
  find: function(store, type, id) 
    var self = this;
    var url = [type,id].join('/');
    return new Ember.RSVP.Promise(function(resolve, reject) 
      Ember.$.ajax(
        url: 'api/' + url,
        type: 'GET'
      ).done(function (response) 
        // cache the response in localStorage
        self.localStoreRecord(response);
        resolve( type: response );
      ).fail(function(jqHXR, responseStatus) 
        reject(new Error(type +
         ' request failed with status=' + reponseStatus);  
      );
    );
  ,
  updateRecord: function(store, type, record) 
    var data = this.serialize(record,  includeId: true );
    var id = record.get('id');
    var url = [type, id].join('/');
    return new Ember.RSVP.Promise(function(resolve, reject) 
      Ember.$.ajax(
        type: 'PUT',
        url: 'api/' + url,
        dataType: 'json',
        data: data
      ).then(function(data) 
        // cache the response in localStorage
        self.localStoreRecord(response);
        resolve( type: response );
      ).fail(function(jqXHR, responseData) 
        reject(new Error(type +
         ' request failed with status=' + reponseStatus);
      );
    );
  
);

// app/initializers/local-storage.js

export var initialize = function(container/*, application*/) 
  var appName = 'myApp';
  var store = container.lookup('store:main');
  var data = JSON.parse(localStorage.getItem(appName));
  console.log('localStorage:',data);
  if (!data) 
    return;
  
  var keys = Object.keys(data);
  if (keys.length) 
    keys.forEach(function(key) 
      console.log(key,data[key][0]);
      store.createRecord(key, data[key][0]);
    );
  
;

export default 
  name: 'local-storage',
  after: 'store',
  initialize: initialize
;

【讨论】:

以上是关于使用 EmberData 在本地存储中缓存远程数据的主要内容,如果未能解决你的问题,请参考以下文章

当我在 Vue-apollo 中使用本地状态时如何获取远程 GraphQL 数据

Docker本地缓存与远程缓存(--cache-from)

Redis 缓存 vs 直接使用内存

高性能的本地缓存方案选型,看这篇就够了!

高性能的本地缓存方案选型,看这篇就够了!

存储器层次结构中的缓存