且构网

分享程序员开发的那些事...
且构网 - 分享程序员编程开发的那些事

使用主干从 DOM 中删除项目时出现内存泄漏

更新时间:2022-10-15 16:29:18

你的代码有几个问题:

  • 您使用 this.$el 作为模型来触发 remove-item 事件.您应该改用您的模型.

  • 视图应该等待来自模型的事件以了解何时移除自身.

这是我想出的代码.如果它不起作用,请将您的代码张贴在某处,以便我自己运行.

var ViewToDelete = Backbone.View.extend({模板:ViewToDelete",tagName: "li",事件:{"点击 .delete-button": "deleteItem"},初始化:函数(){this.listenTo(this.model, 'remove', this.remove);},删除项目:函数(){this.model.remove();}});

视图的

如果你进一步查看代码,你会看到有一个缓存:

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

所以,简而言之,您的代码不会泄漏,但 jQuery 有一个内部缓存,可以防止它被删除.这个缓存只能包含几十个元素,所以它不会永远保留元素.

I am having issues with DOM elements being left in memory after being deleted. I have set-up an example shown below. Note I am using the backbone layout manager plugin to manage my views (as well as jQuery).

I have done a heap snapshot in Chrome before and after deleting one of the items in the list and compared the two:

As you can see the LI element is still in memory.

Backbone Layout Manager does call view.unbind() and view.stopListening() when a view is removed.

Below is the example code.

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>

There are several problems with your code:

  • You use this.$el as model to trigger the remove-item event. You should use your model instead.

  • The view should wait for events from the model to know when to remove itself.

Here's the code I come up with. If it doesn't work, post your code somewhere so I can run it myself.

var ViewToDelete = Backbone.View.extend({
    template: "ViewToDelete",

    tagName: "li",

    events: {
      "click .delete-button": "deleteItem"
    },

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

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

The default implementation of view.remove() will remove this.$el and stop listening to the model:

remove: function() {
  this.$el.remove();
  this.stopListening();
  return this;
},

EDIT: Thank you for posting your code online. Here's what I think is happening (I'm also documenting for future viewers).

If you take a snapshot, filter on Detached DOM Tree, you see:

The important part is the retaining tree: references that prevent the LI from being deleted. The only significant thing is sizzle-1364380997635. It doesn't come from your code, it actually comes from jQuery, more specifically from its Sizzle engine. The key comes from here:

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

If you look further in the code, you see that there's a cache:

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

So, in a nutshell, you code does not leak, but jQuery has an internal cache that prevents it from being removed anyway. This cache can only contain a few dozen elements, so it won't retain elements forever.