使用主干从 DOM 中删除项目时出现内存泄漏 [英] Memory Leak when deleting items from DOM using backbone
问题描述
我遇到了 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 theremove-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屋!