在Backbone.js中实例化子视图时如何避免内存泄漏

时间:2017-01-11 15:52:29

标签: javascript backbone.js

myView =  Backbone.View.extend({
    //event binding etc etc

    render: function() {
        //render some DOM
    }
})

anotherView = Backbone.View.extend({
    events: {
        'click .selector doThis'
    },

    createNewView: function() {
        var view = new myView();
    }
})

createNewView可能会被多次调用。我的理解是,JavaScript的内置垃圾收集不一定会删除变量view,因为它引用了createNewView函数完成时仍然存在的对象/代码。

这是对的吗?怎么处理这个?

我目前的方法是在我的应用级别初始化myView

myApp.view = new myView()

然后在createNewView我只是在这上面调用渲染:

myApp.view.render()

基本上,我只有一个,我重新使用它。

另一种方法是跟踪数组中子视图的创建,然后当我知道不再需要它们时,我依次在每个视图上调用.remove()

我是在正确的轨道上吗?

我发现第二种方法更好,因为如果myViewlistenTo的其他对象上创建绑定回调,则只需重新分配变量就不会删除这些回调。也就是说,如果我正在调用new来实例化视图的新实例,我应该首先在被丢弃的实例上调用remove() ......似乎。

1 个答案:

答案 0 :(得分:4)

在您的示例中,您没有将视图的el放入DOM ,因此没有任何内容引用该视图,然后它将被垃圾收集器收集。

确保视图不再受某种限制的一件好事是在其上调用.remove() 。它将删除:

  • 来自DOM的视图el
  • jQuery DOM events
  • Backbone事件监听器。

Backbone .remove source

// Remove this view by taking the element out of the DOM, and removing any
// applicable Backbone.Events listeners.
remove: function() {
    this._removeElement();
    this.stopListening();
    return this;
},

// Remove this view's element from the document and all event listeners
// attached to it. Exposed for subclasses using an alternative DOM
// manipulation API.
_removeElement: function() {
    this.$el.remove();
},

正如mu is too short in the comments(以及我几乎所有其他答案中所提到的)所提到的,您应始终favor listenTo over on or bind以避免内存泄漏并轻松解除绑定事件。

渲染子视图时,嵌套在父视图中,一个好的做法是保留子视图的数组,以便稍后在每个视图上调用.remove()

简单的列表视图可能如下所示:

var ListView = Backbone.View.extend({
    initialize: function() {
        // Make an array available to keep all the child views
        this.childViews = [];
    },
    addOne: function(model) {
        var view = new Backbone.View({ model: model });

        // as you create new views, keep a reference into the array.
        this.childViews.push(view);

        this.$el.append(view.render().el);
    },

    renderList: function() {
        // replace the view content completely with the template
        this.$el.html(this.templates());

        // then cleanup
        this.cleanup();

        // then render child views
        this.collection.each(this.addOne, this);

        return this;
    },

    cleanup: function() {
        // quick way to call remove on all views of an array
        _.invoke(this.childViews, 'remove');
        // empty the array
        this.childViews = [];
    },
});

虽然如果其他对象正在收听它,它将不会被收集并且可能是泄漏。跟踪引用并在不再需要时删除它们非常重要。