$ .empty()vs Backbone的View.remove()?

时间:2015-06-25 20:27:24

标签: javascript jquery backbone.js

我理解当通过.remove()删除视图时,会在该视图上调用.stopListening()以删除与Backbone中该视图关联的所有事件侦听器。来自Backbone文档:

删除 view.remove()

从DOM中删除视图,并调用stopListening以删除视图已侦听的任何绑定事件

我有一些视图附加到一个容器上,该容器只有通过Backbone的事件钩子才有与dom动作相关的事件。

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

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

我的问题实际上是在一个容器上调用$ .empty()时是否泄漏任何内存(重要或不重要),该容器有效地删除了其中附加的视图。如果我是,那么在这些视图上访问和调用.remove()有什么好的约定吗?

1 个答案:

答案 0 :(得分: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);
}