简体   繁体   中英

Caching collections in backbone.js?

What would be the best way to ensure that my collection stays cached and thereby only gets fetched once?

Should I implement some sort of cache layer? Should I share the Collection variable to wherever it is needed? Can I trust jQuerys AJAX setting? ( $.ajaxSetup ({ cache: true }); )

The basic collection as it looks right now:

theCollection = Backbone.Collection.extend({
    model: theModel,
    url: "source.json"
});

if (typeof myCollection === 'undefined') {
    var myCollection = new theCollection; // Only allow it to be created once
}

I would implement a sort of collection manager in your case:

var manager = (function(){

  var constructors = {
    'example': ExampleCollection
  };
  var collections = {};

  return {
    getCollection: function(name) {
      if(!collections[name]) {
        var collection = new constructors[name]();
        collection.fetch();
        collections[name] = collection;
      }
      return collections[name];
    }
  }
})();

Here the manager is responsible for instantiating collections and fetching them. When you call:

var exampleCollection = manager.getCollection('example');

you get an instance of example collection with data being already fetched. Whenever you need this collection again you can call the method again. You will then get the exact same instance with no need to fetch it again.

This is just a very simple manager example, and there are a lot of additional features you can implement and enhance.

I would strongly advise not to handle this issue on a lower level (eg. the transport layer of $.ajax). If you do that, you would prevent your collection from getting fetched multiple times, but you end up having different model instances with the same id floating around your application. Every collection instance would create it's own models.

In a CouchApp I am currently working on, I also found it necessary to prevent duplicate model instances in different collections (different db views can return the same model data). This has been solved by having a separate collection in the manager, which keeps track of all models already loaded into the application.

Last but not least you might consider implementing a refresh method in your collections or the manager that will handle updating the collection from the server. If you do this with the fetch method your whole collection is reseted so all models are destroyed and then recreated. This is bad if you have models from this collection referenced somewhere else in the app (as you typically do). Those instances are outdated and duplicated in your app then. The refresh method checks wether instances with the incoming id are already present in the curent collection. If so they are updated, otherwise they are added.

If by caching you actually mean a singleton, so that you can reference the same domain list from multiple places in a modular JS application, we use RequireJS for this. You can separate your collection to be a module in the application, which you then require wherever you are using it. In pseudocode:

require(["myCollection"],
    function(myCollection) {
        var MyView = Backbone.View.extend();
        new MyView({
            collection: myCollection
        }).render();
    }
);

Your callback function will always get the same instance you returned when you define your myCollection module. Bind to that instance from all your views, and whenever it is refreshed, those views will get an event trigger and can update themselves.

You can try to save your collection in localStorage. Here is the link ( http://documentcloud.github.com/backbone/#examples-todos ).

The app uses a LocalStorage adapter to transparently save all of your todos within your browser, instead of sending them to a server.

I hope it helps :)

A simple solution would be to make a global variable for your collection so that all your javascript code will use the same variable.

Just fetch the collection when the page loads

$(function() {
  myCollection = theCollection();
  myCollection.fetch();
});

Since var is not used to declared myCollection , then it becomes a global variable.

Of course this is a simple implementation. There are more robust implementations, but depending on your needs, they might be overkill.

I ended up doing something similar to ProTom's solution. Instead of dynamically initializing a collection based on the name, I decided to just use a function to setup the collection. Through my application, I needed to initialize collections differently depending on where it was coming from. This proved to be the best way for my needs. Here's the CoffeeScript:

Cache:

cachedCollections: {}
getCollection: (key, block) ->
  collection = @cachedCollections[key]
  return collection if collection
  collection = block()
  @cachedCollections[key] = collection
  collection

Usage:

commentCollection = getCollection "comments-#{postId}", ->
  collection = new CommentCollection
  collection.url = "/api/posts/#{postId}/comments"
  collection

You can try out

https://github.com/mrappleton/backbone-fetch-cache

It also has expires support that is easy to set maximum time of the cache.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM