Утечка памяти при удалении элементов из DOM с использованием магистрали

У меня проблемы с тем, что элементы DOM остаются в памяти после удаления. Я настроил пример, показанный ниже. Обратите внимание, что я использую плагин Backbone Layout Manager для управления своими представлениями (а также jQuery).

Я сделал снимок кучи в Chrome до и после удаления одного из элементов в списке и сравнил два:

Сравнение снимков кучи Chrome

Как видите, элемент LI все еще находится в памяти.

Backbone Layout Manager выполняет вызовы view.unbind() и view.stopListening() при удалении представления.

Ниже приведен пример кода.

ListOfViewsToDelete.js

var TestModel = Backbone.Model.extend({
  });

  var TestCollection = Backbone.Collection.extend({
    model: TestModel,
  });

  var ViewToDelete = Backbone.View.extend({
    template: "ViewToDelete",
    tagName: "li",
    events: {
      "click .delete-button": "deleteItem"
    },
    deleteItem: function() {
      this.$el.trigger('remove-item', [this.model.id]);
    }
  });

  var ListOfViewsToDelete = Backbone.View.extend({
    template: "ListOfViewsToDelete",
    initialize: function() {
      this.collection = new TestCollection();

      for (var i = 0; i < 5; i++) {
        this.collection.add(new TestModel({id: i}));
      }

      this.listenTo(this.collection, 'all', this.render);
    },
    events: {
      "remove-item": "removeItemFromCollection"
    },
    beforeRender: function() {

      this.collection.each(function(testModel) {
        this.insertView("ul", new ViewToDelete({
          model: testModel
        }));
      }, this);

    },
    removeItemFromCollection: function(event, model) {
      this.collection.remove(model);
    }
  });

router.js

app.useLayout("MainLayout").setViews({
                    "#main": new ListOfViewsToDelete()
                }).render();

ListOfViewsToDelete.html

<ul>
</ul>

ViewToDelete.html

View to delete
<button class="delete-button">x</button>

1 ответ

Решение

Есть несколько проблем с вашим кодом:

  • Ты используешь this.$el как модель, чтобы вызвать remove-item событие. Вы должны использовать свою модель вместо этого.

  • Представление должно ждать событий от модели, чтобы знать, когда удалить себя.

Вот код, который я придумаю. Если это не работает, опубликуйте свой код где-нибудь, чтобы я мог запустить его сам.

var ViewToDelete = Backbone.View.extend ({
    шаблон: "ViewToDelete",

    tagName: "li",

    События: {
      "click .delete-button": "deleteItem"
    },

    initialize: function () {
      this.listenTo(this.model, 'удалить', this.remove);
    },

    deleteItem: function() {
      this.model.remove();
    }
});

Реализация по умолчанию view.remove() удалит this.$el и перестать слушать модель:

удалить: функция () {
  это $el.remove ().
  this.stopListening ();
  верни это;
},

РЕДАКТИРОВАТЬ: Спасибо за размещение вашего кода в Интернете. Вот что я думаю, что происходит (я также документирую для будущих зрителей).

Если вы сделаете снимок, отфильтровываете в Detached DOM Tree, вы увидите:

Снимок веб-инспектора

Важной частью является сохраняющее дерево: ссылки, которые препятствуют удалению LI. Единственная существенная вещь sizzle-1364380997635, Он не исходит из вашего кода, он на самом деле исходит от jQuery, точнее от его движка Sizzle. Ключ приходит отсюда:

https://github.com/jquery/sizzle/blob/master/sizzle.js#L33

Если вы посмотрите дальше в коде, вы увидите, что есть кеш:

https://github.com/jquery/sizzle/blob/master/sizzle.js#L1802

Итак, в двух словах, ваш код не протекает, но jQuery имеет внутренний кеш, который все равно не позволяет удалить его. Этот кэш может содержать только несколько десятков элементов, поэтому он не будет сохранять элементы вечно.

Другие вопросы по тегам