$.empty() 对比 Backbone 的 View.remove()?
$.empty() vs Backbone's View.remove()?
我了解当通过 .remove() 删除视图时,会在该视图上调用 .stopListening() 以删除 Backbone 中与该视图关联的任何事件侦听器。来自 Backbone 文档:
删除 view.remove()
从 DOM 中删除一个视图,并调用 stopListening 以删除该视图已监听的任何绑定事件 。
我有附加到容器的视图,该容器仅包含与通过 Backbone 的事件挂钩对自身执行的 dom 操作相关的事件。
var View = Backbone.View.extend({
events : {
'input keyup' : 'searchDropdown'
},
searchDropdown: function () {
$('dropdown').empty();
//Appends views based on search
}
});
我的问题是,当我在一个容器上调用 $.empty() 时,我是否泄漏了任何内存(重要的或不重要的),该容器有效地删除了附加在其中的视图。如果我是,在这些视图上访问和调用 .remove() 是否有任何好的约定?
您不需要为此使用任何特殊框架,但正确实施删除是个好主意,而不是依赖于足够智能的浏览器来执行此操作。有时在大型应用程序中,您会发现您特别需要重写 remove 方法来执行一些特殊的清理工作——例如,您在该视图中使用的库具有 destroy
方法。
现代浏览器往往有一个 GC,它在大多数情况下都足够智能,但我仍然不想依赖它。最近我参与了 Backbone 中的一个项目,该项目没有子视图的概念,我通过从 empty
更改为 remove
将泄漏节点减少了 50%(在 Chrome 43 ).很难让大型 javascript 应用程序不泄漏内存,我的建议是尽早对其进行监控:If a DOM Element is removed, are its listeners also removed from memory?
注意会泄漏大量内存的东西 - 例如图像。我在一个项目上有一些代码做了这样的事情:
var image = new Image();
image.onLoad(.. reference `image` ..)
image.src = ...
基本上是一个预加载器。因为我们没有明确地执行 image = null
GC 从未启动,因为回调引用了 image
变量。在图像繁重的网站上,我们会在每次页面转换时泄漏 1-2mb,这会导致手机崩溃。在 remove
覆盖中将变量设置为 null 修复了此问题。
在子视图上调用 remove 就像做这样的事情一样简单:
remove: function() {
this.removeSubviews();
Backbone.View.prototype.remove.call(this);
},
removeSubviews: function() {
if (!_.isEmpty(this.subViews)) {
_.invoke(this.subViews, 'remove');
this.subViews = [];
}
}
您只需要将您的子视图实例添加到一个数组中。例如,当您创建一个子视图时,您可以使用 parentView: this
之类的选项并将其添加到父视图的数组中。我过去做过更复杂的子视图系统,但那会很好用。在初始化视图时,您可以执行以下操作:
var parentView = this.options.parentView;
if (parentView) {
(parentView.subViews = parentView.subViews || []).push(this);
}
我了解当通过 .remove() 删除视图时,会在该视图上调用 .stopListening() 以删除 Backbone 中与该视图关联的任何事件侦听器。来自 Backbone 文档:
删除 view.remove()
从 DOM 中删除一个视图,并调用 stopListening 以删除该视图已监听的任何绑定事件 。
我有附加到容器的视图,该容器仅包含与通过 Backbone 的事件挂钩对自身执行的 dom 操作相关的事件。
var View = Backbone.View.extend({
events : {
'input keyup' : 'searchDropdown'
},
searchDropdown: function () {
$('dropdown').empty();
//Appends views based on search
}
});
我的问题是,当我在一个容器上调用 $.empty() 时,我是否泄漏了任何内存(重要的或不重要的),该容器有效地删除了附加在其中的视图。如果我是,在这些视图上访问和调用 .remove() 是否有任何好的约定?
您不需要为此使用任何特殊框架,但正确实施删除是个好主意,而不是依赖于足够智能的浏览器来执行此操作。有时在大型应用程序中,您会发现您特别需要重写 remove 方法来执行一些特殊的清理工作——例如,您在该视图中使用的库具有 destroy
方法。
现代浏览器往往有一个 GC,它在大多数情况下都足够智能,但我仍然不想依赖它。最近我参与了 Backbone 中的一个项目,该项目没有子视图的概念,我通过从 empty
更改为 remove
将泄漏节点减少了 50%(在 Chrome 43 ).很难让大型 javascript 应用程序不泄漏内存,我的建议是尽早对其进行监控:If a DOM Element is removed, are its listeners also removed from memory?
注意会泄漏大量内存的东西 - 例如图像。我在一个项目上有一些代码做了这样的事情:
var image = new Image();
image.onLoad(.. reference `image` ..)
image.src = ...
基本上是一个预加载器。因为我们没有明确地执行 image = null
GC 从未启动,因为回调引用了 image
变量。在图像繁重的网站上,我们会在每次页面转换时泄漏 1-2mb,这会导致手机崩溃。在 remove
覆盖中将变量设置为 null 修复了此问题。
在子视图上调用 remove 就像做这样的事情一样简单:
remove: function() {
this.removeSubviews();
Backbone.View.prototype.remove.call(this);
},
removeSubviews: function() {
if (!_.isEmpty(this.subViews)) {
_.invoke(this.subViews, 'remove');
this.subViews = [];
}
}
您只需要将您的子视图实例添加到一个数组中。例如,当您创建一个子视图时,您可以使用 parentView: this
之类的选项并将其添加到父视图的数组中。我过去做过更复杂的子视图系统,但那会很好用。在初始化视图时,您可以执行以下操作:
var parentView = this.options.parentView;
if (parentView) {
(parentView.subViews = parentView.subViews || []).push(this);
}