使用主干从 DOM 中删除项目时出现内存泄漏 [英] Memory Leak when deleting items from DOM using backbone

查看:19
本文介绍了使用主干从 DOM 中删除项目时出现内存泄漏的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我遇到了 DOM 元素被删除后仍留在内存中的问题.我已经设置了一个如下所示的示例.注意我使用主干布局管理器插件来管理我的视图(以及 jQuery).

我在删除列表中的一项之前和之后在Chrome中做了一个堆快照并比较了两者:

如您所见,LI 元素仍在内存中.

当视图被移除时,主干布局管理器会调用 view.unbind() 和 view.stopListening().

下面是示例代码.

ListOfViewsToDelete.js

var TestModel = Backbone.Model.extend({});var TestCollection = Backbone.Collection.extend({模型:测试模型,});var ViewToDelete = Backbone.View.extend({模板:ViewToDelete",tagName: "li",事件:{"点击 .delete-button": "deleteItem"},删除项目:函数(){this.$el.trigger('remove-item', [this.model.id]);}});var ListOfViewsToDelete = Backbone.View.extend({模板:ListOfViewsToDelete",初始化:函数(){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);},事件:{"remove-item": "removeItemFromCollection"},渲染前:函数(){this.collection.each(函数(测试模型){this.insertView("ul", new ViewToDelete({型号:testModel}));}, 这);},removeItemFromCollection:函数(事件,模型){this.collection.remove(model);}});

router.js

app.useLayout("MainLayout").setViews({#main":新的 ListOfViewsToDelete()}).使成为();

ListOfViewsToDelete.html

    ViewToDelete.html

    查看删除<button class="delete-button">x</button>

    解决方案

    你的代码有几个问题:

    • 您使用 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.

    这篇关于使用主干从 DOM 中删除项目时出现内存泄漏的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

    查看全文
    登录 关闭
    扫码关注1秒登录
    发送“验证码”获取 | 15天全站免登陆