3

バックボーン コレクションで動作するフィルターがあります。検索ボックスに検索を入力し、ライブ フィルターを一覧表示します。素晴らしい作品、またはそう思った。Chrome でメモリ ヒープのスナップショットを見ると、検索のたびにメモリ リークが発生していることがわかります... 6 メガ、8 メガ、やがてヒープ スナップショットが 100 メガ以上になります。

以下のビューで問題を特定しました。初期化関数で this.listenTo をコメントアウトすると、メモリリークが発生しなくなりました。

したがって、私の質問は、これらのイベント リスナーとライブ フィルタリングをコレクションにリークさせずに保持するにはどうすればよいかということです。

var View = Backbone.View.extend({

    tagName: 'tr',

    initialize: function() {
        this.listenTo(this.model, 'change', this.render);
        this.listenTo(this.model, 'destroy', this.remove);
    },

    events: {
        'click .edit': 'edit',
        'click .delete': 'delete',
    },

    edit: function() { /* EDIT */ },

    delete: function() {
        this.model.destroy(); //backbone
    },

    render: function () {
        var template = _.template( ProductTemplate )
        this.$el.html( template({ this.model.toJSON() }) )
        return this;
    }

})


var ListView = Backbone.View.extend({

    initialize: function()
    {
        this.collection = new Collection( Products ) //products are bootstrapped on load
    },

    render: function (terms)
    {
        this.$el.html( ListTemplate );

        var filtered = Shop.products.collection.search(terms)

        _.each(filtered, this.addOne, this)

        //append list to table
        $('#products').html( this.el )

        return this
    },

    addOne: function (product)
    {
        this.$el.find('tbody').append(
            new View({ model: product }).render().el
        )

        return this
    },

});

var Collection = Backbone.Collection.extend({

    model: Model,

    search : function(letters){

        //set up a RegEx pattern
        var pattern = new RegExp(letters,"gi")

        //filter the collection
        return this.filter(function(model)
        {
            if(letters == "") return true //if search string is empty return true
            return pattern.test(model.attributes['Product']['name'])
        });
    }


});

解決済み:

これが私の新しい検索方法です。コレクションをフィルタリングして再レンダリングすることはもうありません。コレクションを単純にループし、モデルが検索に一致する場合は「表示」イベントをトリガーし、検索にない場合は「非表示」イベントをトリガーします。次に、ビューでこれらのイベントをサブスクライブし、それに応じて動作します。

コレクションからの検索機能: search : function(query){

    //set up a RegEx pattern
    var pattern = new RegExp(query,"gi")

    //filter the collection
    this.each(function(model){
      if ( pattern.test(model.attributes['Product']['name']) ){
        model.trigger('show')
      }
      else{
        model.trigger('hide')
      }
});
}

新しいビュー: var ProductView = Backbone.View.extend({

    tagName: 'tr',

    initialize: function() {
        this.listenTo(this.model, 'show', this.show);
        this.listenTo(this.model, 'hide', this.hide);
    },

    hide: function()
    {
      this.$el.addClass('hide')
    },

    show: function()
    {
      this.$el.removeClass('hide')
    },

    render: function ()
    {
        var template = _.template( ProductTemplate )
        this.$el.html( template( {data: this.model.toJSON(), Utils: Shop.utils} ) )
        return this;
    }

});
4

1 に答える 1

4

@muがすでにコメントしている内容を拡張するために、作成したビューを削除するのではありません。それらはDOMにはありませんが、モデルへの参照があるため、まだメモリ内でぶらぶらしています(したがって、ガベージコレクターはそれらを削除しません)。

いくつかのオプションがあります:

  1. によってインスタンス化されているすべてのビューを追跡し、が呼び出されるaddOneたびにそれらを削除します。render
  2. フィルタ条件が変更されるたびにインスタンス化/破棄するのではなく、コードでビューを表示/非表示にします。これはより多くの作業ですが、確かにより最適なソリューションです。
于 2013-01-20T05:31:50.127 に答える