Backbone.js : repopulate or recreate the view?

I always destroy and create views because as my single page app gets bigger and bigger, keeping unused live views in memory just so that I can re-use them would become difficult to maintain.

Here’s a simplified version of a technique that I use to clean-up my Views to avoid memory leaks.

I first create a BaseView that all of my views inherit from. The basic idea is that my View will keep a reference to all of the events to which it’s subscribed to, so that when it’s time to dispose the View, all of those bindings will automatically be unbound. Here’s an example implementation of my BaseView:

var BaseView = function (options) {

    this.bindings = [];
    Backbone.View.apply(this, [options]);
};

_.extend(BaseView.prototype, Backbone.View.prototype, {

    bindTo: function (model, ev, callback) {

        model.bind(ev, callback, this);
        this.bindings.push({ model: model, ev: ev, callback: callback });
    },

    unbindFromAll: function () {
        _.each(this.bindings, function (binding) {
            binding.model.unbind(binding.ev, binding.callback);
        });
        this.bindings = [];
    },

    dispose: function () {
        this.unbindFromAll(); // Will unbind all events this view has bound to
        this.unbind();        // This will unbind all listeners to events from 
                              // this view. This is probably not necessary 
                              // because this view will be garbage collected.
        this.remove(); // Uses the default Backbone.View.remove() method which
                       // removes this.el from the DOM and removes DOM events.
    }

});

BaseView.extend = Backbone.View.extend;

Whenever a View needs to bind to an event on a model or collection, I would use the bindTo method. For example:

var SampleView = BaseView.extend({

    initialize: function(){
        this.bindTo(this.model, 'change', this.render);
        this.bindTo(this.collection, 'reset', this.doSomething);
    }
});

Whenever I remove a view, I just call the dispose method which will clean everything up automatically:

var sampleView = new SampleView({model: some_model, collection: some_collection});
sampleView.dispose();

I shared this technique with the folks who are writing the “Backbone.js on Rails” ebook and I believe this is the technique that they’ve adopted for the book.

Update: 2014-03-24

As of Backone 0.9.9, listenTo and stopListening were added to Events using the same bindTo and unbindFromAll techniques shown above. Also, View.remove calls stopListening automatically, so binding and unbinding is as easy as this now:

var SampleView = BaseView.extend({

    initialize: function(){
        this.listenTo(this.model, 'change', this.render);
    }
});

var sampleView = new SampleView({model: some_model});
sampleView.remove();

Leave a Comment