简体   繁体   English

在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 may be called multiple times. createNewView可以被多次调用。 My understanding is that the variable view will not necessarily be removed by JavaScript's built-in garbage collection because it references objects/code which still exists when the createNewView function completes. 我的理解是,变量view不一定会被JavaScript的内置垃圾回收删除,因为它引用了createNewView函数完成时仍然存在的对象/代码。

Is this correct? 这个对吗? How to deal with this? 该如何处理?

My current approach is to initialise myView once at the level of my app: 我目前的方法是在应用程序级别一次初始化myView

myApp.view = new myView()

Then in createNewView I just call render on this: 然后在createNewView我仅对此调用render:

myApp.view.render()

Essentially, I only ever have one of them and I re-use it. 本质上,我只有其中之一,并且我会重复使用它。

An alternative approach is to track the creation of sub views in an array and then I call .remove() on each one in turn when I know they are no longer needed. 另一种方法是跟踪数组中子视图的创建,然后在我知道不再需要它们时依次对每个视图调用.remove()

Am I on the right track? 我在正确的轨道上吗?

It occurs to me that the second approach is better because if myView created bound callbacks on other objects with listenTo , these would not be removed simply by re-assigning the variable. 在我看来,第二种方法更好,因为如果myView使用listenTo在其他对象上创建了绑定回调,则仅通过重新分配变量就不会删除这些回调。 That is, if I am calling new to instantiate a new instance of the view, I should call remove() on the being discarded instance first... It seems. 也就是说,如果我要调用new来实例化视图的新实例,则应首先在被丢弃的实例上调用remove() ……似乎。

In your example, you don't put the view's el into the DOM , so nothing is referencing the view, then it will be collected by the garbage collection. 在您的示例中,您没有将视图的el放入DOM ,因此没有任何东西引用该视图,然后它将被垃圾回收器收集。

One good thing to ensure a view isn't bound to something anymore is to call .remove() on it . 确保视图不再绑定于某物的一个好处是在其上调用.remove() It will remove: 它将删除:

  • the view's el from the DOM, 视图的el从DOM,
  • the jQuery DOM events jQuery DOM事件
  • the Backbone event listeners. Backbone事件监听器。

The Backbone .remove source : 骨干.remove来源

 // 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(); }, 

As mentioned by mu is too short in the comments (and myself in almost every other answers), you should always favor listenTo over on or bind to avoid memory leaks and ease unbinding events. 正如mu所提到的那样,注释太短 (我自己几乎在所有其他答案中),您应该始终偏爱listenTo不是onbind以避免内存泄漏并简化解除绑定的事件。

When rendering child views, nested inside a parent view, a good practice is to keep an array of the child views to later call .remove() on each of them. 渲染嵌套在父视图内的子视图时,一个好的做法是保留一个子视图数组,以便以后在每个子视图上调用.remove()

A simple list view might look like this: 一个简单的列表视图可能如下所示:

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 = [];
    },
});

Though if other objects are listening to it, it won't be collected and may be a leak. 虽然如果其他对象在听,则不会被收集,可能是泄漏。 It's important to keep track of the references and delete them all when you don't need it anymore. 重要的是要跟踪引用,并在不再需要它们时将其全部删除。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM