65.9K
CodeProject 正在变化。 阅读更多。
Home

UpdatePanel 中的 jQuery 内存泄漏

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.74/5 (21投票s)

2009年3月20日

CPOL

4分钟阅读

viewsIcon

89810

解决 UpdatePanel 中的 jQuery 内存泄漏问题。

引言

本文讨论了如何将 jQuery 库与 Microsoft AJAX.NET 集成,特别是与 <asp:UpdatePanel/> 集成,以及如何避免 Internet Explorer 在此过程中出现内存泄漏。

jQuery 变得越来越流行。然而,Microsoft ASP.NET 和 AJAX.NET 提供了它们自己的与服务器通信的框架。在 ASP.NET 环境中,jQuery AJAX 机制看起来笨拙且不自然。使用 ASP.NET UpdatePanels 进行 AJAX 功能,并使用 jQuery 进行其 selectors 功能要容易得多——这正是我想象中 Microsoft 计划要做的事情。然而,这样做很棘手,本文解释了这样做的一些不足之处。

迭代 1 - 示例页面

让我们想象一个示例计算器页面,用于将两个数字相加

<body> 
    <form id="form1" runat="server"> 
   	<asp:ScriptManager runat="server" ScriptMode=Debug> 
   	    <Scripts> 
		<asp:ScriptReference Path="~/jquery-1.2.6.debug.js" /> 
	    </Scripts> 
	</asp:ScriptManager> 
	<asp:UpdatePanel ID='up' runat="server"><ContentTemplate> 
	    <asp:TextBox ID='i1' runat="server" CssClass='num' Width='50px'/>+ 
	    <asp:TextBox ID='i2' runat="server" CssClass='num' Width='50px'/>= 
	    <asp:TextBox ID='res' runat="server" Width='50px'/> 
	    <asp:Button ID='btn' runat="server" Text='...'/> 
	</ContentTemplate></asp:UpdatePanel> 
    </form> 
</body> 
<script> 
	function add() { 
		$get('res').value = parseInt($get('i1').value) 
			+ parseInt($get('i2').value); 
	} 
	$(document).ready(function() { 
		$('.num').change(add); 
	}); 
</script> 

如果您熟悉 ASP.NET 和 jQuery,则此页面不应需要太多解释

  • 我们包含 ScriptManager jquery 脚本。
  • 标准表单
  • 我们定义了 3 个输入控件,前两个输入控件的 class='num' - 因此我们可以使用 jQuery 轻松找到它们
  • 内容 放置在 <asp:UpdatePanel> 中,并提供一个按钮来刷新面板
  • 定义了一个函数,用于获取 2 个数字,将它们相加,并将结果存储在第三个输入框中
  • 提供了标准的 jQuery 机制来附加 change 事件,因此当其中一个数字发生更改时,将执行计算 。

生成的页面如下所示

最初,这些东西似乎有效。您可以更改数字,并在屏幕上看到结果。但是,尝试刷新 UpdatePanel - 然后突然计算中断。

解释

UpdatePanel 刷新后失败的原因是 UpdatePanel 的内容以及附加到它们的所有事件都消失了。
要解决此问题,我们需要使用 AJAX.NET 提供的将事件附加到页面的方法 (这里我只附加了脚本部分)

function add() {
	$get('res').value = 
		parseInt($get('i1').value) + parseInt($get('i2').value);
}
Sys.Application.add_load(function() {
	$('.num').change(add);
});

迭代 2 - 内存泄漏

迭代 1 中建议的方法对我的作用持续了一段时间。除了最终,我开始注意到 Internet Explorer 变慢了。非常慢。然后,查看进程监视器,我注意到 Internet Explorer 会在类似兆字节的 UpdatePanel 刷新中泄漏内存。查看 sIEve 工具(感谢创建者),我发现输入元素没有被销毁

您可以看到,每次单击刷新时,它都会创建一个新的输入元素集,而没有销毁之前的元素。

解释

在 Google 上搜索一段时间后,我注意到 Internet Explorer 内存泄漏是许多开发人员的痛点。大多数投诉被确定为伪泄漏(内存实际上被释放,但您需要完全刷新页面才能看到它)或作为 closures

closure 是一个有趣的话题。它指的是附加到 DOM 元素的 JavaScript 事件。显然,Internet Explorer 使用 2 个垃圾回收器 - 一个用于 DOM,另一个用于 JavaScript。其中涉及一些语义,但底线是,如果您将事件附加到 DOM 对象,则任何一个垃圾回收器都无法摆脱涉及的对象/DOM 元素。我强烈建议您在 WEB 上阅读更多关于闭包的信息。

好吧,jQuery 就是关于闭包的。它们很好地附加到 window.unload 并在页面卸载时进行清理,但它们不知道 UpdatePanel。当 UpdatePanel 即将刷新时,需要做些事情来清理 jQuery

迭代 3 - jQuery 清理插件

UpdatePanel 确实提供了清理钩子。当它即将删除一个元素时,它会检查是否存在 element.dispose 函数。如果存在这样的函数,则它将被执行。

所以我最终创建了以下 jQuery 插件来处理这种情况

(function($) {
	$.fn.Disposable = function(cln) {
		return this.each(function() {
			var el = this;
			if (!el.dispose) {
				el.dispose = cleanup; // will be called by 
						    // Microsoft for cleanup
				$(window).bind("unload", cleanup);
			}
			function cleanup() {
				if (!el)
					return;
				$(el).unbind();
				$(window).unbind("unload", cleanup);
				el.dispose = null;
				el = null;
			};
		});
	};
})(jQuery);
function add() {
	$get('res').value = 
		parseInt($get('i1').value) + parseInt($get('i2').value);
}
Sys.Application.add_load(function() {
	$('.num').change(add).Disposable();
});

如您所见,此 jQuery 插件将 cleanup() 函数附加到 element.dispose。每当调用 element.dispose 时,所有 jQuery 事件都将从该元素中取消绑定,并允许垃圾回收器回收所有对象。

总结 

我正在讨论的代码实际上是我之前在 基于 jQuery 的 Ajax.Net 库 中发布的另一个库的一部分。欢迎您下载该项目并在闲暇时浏览代码。

历史

  • 2009 年 3 月 20 日:初始发布
© . All rights reserved.