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

How to avoid a memory leak when instantiating child views in 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 可能会被多次调用。我的理解是,变量 view 不一定会被 JavaScript 的内置垃圾收集器删除,因为它引用 objects/code,而 objects/code 在 createNewView 函数完成时仍然存在。

这是正确的吗?如何处理?

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

myApp.view = new myView()

然后在 createNewView 中我只是调用渲染:

myApp.view.render()

基本上,我只有其中之一,我会重复使用它。

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

我走在正确的轨道上吗?

我觉得第二种方法更好,因为如果 myView 使用 listenTo 在其他对象上创建绑定回调,则不会通过重新分配变量简单地删除这些回调。也就是说,如果我调用 new 来实例化视图的新实例,我应该先在被丢弃的实例上调用 remove() ……看来。

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

确保视图不再绑定到某物的一件好事是 对其调用 .remove()。它将删除:

  • 视图的 el 来自 DOM、
  • jQueryDOM事件
  • 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();
},

(and myself in almost every other answers), you should always 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 = [];
    },
});

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