4

I understand that when a view is removed through .remove(), .stopListening() is called on that view to remove any event listeners associated with that view in Backbone. From the Backbone docs:

remove view.remove()

Removes a view from the DOM, and calls stopListening to remove any bound events that the view has listenTo'd.

I have views that are appended to a container that only have events related to dom actions on themselves through Backbone's events hook.

var View = Backbone.View.extend({
   events : {
      'input keyup' : 'searchDropdown'
   },

   searchDropdown: function () {
      $('dropdown').empty();
      //Appends views based on search
   }
});

My question is really whether or not I'm leaking any memory (significant or not) when calling $.empty() on a container that effectively removes the view(s) appended inside of it. And if I am, is there any good convention for accessing and calling .remove() on those views?

Justin Hathaway
  • 239
  • 1
  • 8

1 Answers1

2

You don't need any special framework for this but it's a good idea to implement removal properly and not depend on the browser being smart enough to do this. Sometimes in a large app you will find you specifically need to override the remove method to do some special cleanup - for instance you are using a library in that view which has a destroy method.

A modern browser tends to have a GC which is smart enough for most cases but I still prefer not to rely on that. Recently I came on to a project in Backbone which had no concept of subviews and I reduced the leaking nodes by 50% by changing to remove from empty (in Chrome 43). It's very hard to have a large javascript app not leak memory, my advice is to monitor it early on: If a DOM Element is removed, are its listeners also removed from memory?

Watch out for things which leak a lot of memory - like images. I had some code on a project that did something like this:

var image = new Image();

image.onLoad(.. reference `image` ..)

image.src = ...

Basically a pre-loader. And because we weren't explicitly doing image = null the GC never kicked in because the callback was referencing the image variable. On an image heavy site we were leaking 1-2mb with every page transition which was crashing phones. Setting the variable to null in a remove override fixed this.

Calling remove on subviews is as easy as doing something like this:

remove: function() {
    this.removeSubviews();
    Backbone.View.prototype.remove.call(this);
},

removeSubviews: function() {
    if (!_.isEmpty(this.subViews)) {
         _.invoke(this.subViews, 'remove');
         this.subViews = []; 
    }
}

You just need to add your subview instances to an array. For example when you create a subview you could have an option like parentView: this and add it to the array of the parent. I have done more intricate subview systems in the past but that would work fine. On initialize of the views you could do something like:

var parentView = this.options.parentView;
if (parentView) {
    (parentView.subViews = parentView.subViews || []).push(this);
}
Dominic
  • 62,658
  • 20
  • 139
  • 163