AJAX 应用程序体系结构3

剖析 AJAX 回发

若要使 ASP.NET 页面成为部分呈现的页面,首先必须向页面添加一个脚本管理器,然后通过使用 UpdatePanel 控件进行封装,从而定义可独立更新的区域。例如:

< ?xml:namespace prefix = asp />
< asp:ScriptManager runat="server">< /asp:ScriptManager>
< asp:UpdatePanel id=UpdatePanel1 runat="server">
< CONTENTTEMPLATE>
< %-- Markup of the region goes here --%>
< /CONTENTTEMPLATE>
< /asp:UpdatePanel>

UpdatePanel 控件不会以任何方式改变为该区域生成的可见标记,而只是向原始标记添加一个外围

标记:

< DIV id=UpdatePanel1>
< %-- Markup of the region goes here --%>
< /DIV>

什么会触发 AJAX 回发?如何进行管理?由谁管理?每当脚本管理器在页面上检测到一个或多个 UpdatePanel 控件时,它会发出如下的脚本程序块:

< SCRIPT type=text/javascript>
Sys.WebForms.PageRequestManager._initialize('ScriptManager1',
document.getElementById('form1'));
Sys.WebForms.PageRequestManager.getInstance()._updateControls(
['tUpdatePanel1','tUpdatePanel2'], [], [], 90);
< /SCRIPT>

_initialize 方法是客户端 PageRequestManager 对象上的静态方法(参见 MicrosoftAjaxWebForms.js)。它会创建一个 PageRequestManager 类的全局实例,并将其初始化。该类充当一个单例,并且是后来可通过 getInstance 方法来检索的唯一可用实例。上述代码段的第二个语句向客户端框架注册了一组 UpdatePanel 控件。每个服务器端 UpdatePanel 控件都通过其 ID 进行引用。

此处发生的关键操作在 _initialize 方法内。如上所述,在创建该类的单个实例后,该代码会将其初始化。此时,还为 DOM 表单对象的提交事件注册了一个处理程序。这意味着,每当页面提交表单时,AJAX 脚本会介入并使用 XMLHttpRequest 发出请求,而不是让请求经历普通的浏览器回发。最初的表单字段集保持不变,并且为服务器端脚本管理器方便起见,会附加一些额外信息。因此,AJAX 回发上载的信息要比常规 ASP.NET 回发多一点。

视图状态以及任何其他隐藏字段随请求一起执行并上载到服务器。返回途中,会一同下载更新的视图状态与新的隐藏字段及可能更短的标记(如果有)。特别是,该响应只包括回发期间修改的可更新区域的标记。该列表包括触发回发的 UpdatePanel 控件(和所有嵌套面板)、页面中 UpdateMode 属性设置为 Always 的任何其他 UpdatePanel 控件,以及以编程方式刷新的所有 UpdatePanel 控件。以下代码是如何根据运行时情况以编程方式刷新面板的一个示例:

   UpdatePanel1.Update()

请考虑图4 中所示的示例页面和返回的响应。您可以使用各种工具来监视入站和出站的 HTTP 数据包。此专栏中,我使用的是 Nikhil Kothari 的 Web Development Helper 工具,您可以从 projects.nikhilk.net/Projects/WebDevHelper.aspx 免费下载。

AJAX 回发的响应是一个文本流,可将其视为具有大小、类型、ID 和内容列的记录表格。每条记录都称为一个 delta 节点。图5 显示了您获得的示例页面表格。

delta 节点标识了 AJAX 回发期间可能出现的更改。并非所有的响应都有相同的 delta 节点集,这取决于请求的服务器生命周期内发生的事件及原始标记的结构。

图 6 列出了目前支持的所有 delta 节点。负责处理 delta 表格的 JavaScript 代码位于 MicrosoftAjaxWebForms.js 文件中。

部分呈现的优化技术

查看图6 之后,您会发现响应由两大块组成:标记和视图状态。事件验证数据(ASP.NET 2.0 的安全相关功能)、自定义隐藏字段、脚本和任何其他类型的节点通常总共只有几十个字节。视图状态的大小是 ASP.NET 页面的老问题了。遗憾的是,使用 ASP.NET AJAX 的部分呈现并未修正该问题。您仍需要精简您的视图状态,从而减少下载时间。那么如何减少页面的标记呢?实际上,您可以使用更小的面板来恢复特定点击所需的最少标记数量。

再次查看图4 中的代码。页面中的逻辑相当简单,但一针见血。基本上,用户单击按钮时,TextBox 的所有内容都会用来更新标签。

页面只包含一个涵盖以下所有内容的 UpdatePanel 控件:文本框、按钮、分隔符和标签。逻辑上来说,这是可以接受的。如果您需要指定让页面中某个区域自行刷新,这是一个可供选择的方案。但是,在这种方式定义下,该区域包含了在回发中没有更新的重要部分代码。例如,TextBox 和 Button 控件不在此页面中更新,且分隔符为静态纯文本。

更好的方式是减少 UpdatePanel,以便包含唯一的 Label 控件 — 由按钮触发的任意回发中更新的唯一服务器控件。通常,您需要仔细确定控件之间的回发关系(基本上检查哪个更新哪个),并设计可更新面板,以包括用于指定用户操作的最少控件数。在图4 所示的示例上下文中,有一个更佳方案:

< ?xml:namespace prefix = asp />
< asp:Button id=Button1 onclick=Button1_Click runat="server" Text="Update">
< HR>
< asp:UpdatePanel id=UpdatePanel1 runat="server">
< CONTENTTEMPLATE>
< asp:Label id=Label1 runat="server">
< /CONTENTTEMPLATE>
< TRIGGERS>
< asp:AsyncPostBackTrigger ControlID="Button1">
< /TRIGGERS>
< /asp:UpdatePanel>

现在 UpdatePanel 控件只包含 Label 控件。这意味着文本框和按钮控件的(未更改)标记将不会返回。默认情况下,UpdatePanel 控件会在以下情况下刷新:它的任一个子控件引起回发或页面上另一 UpdatePanel 进行了刷新。 此行为可通过某些公共属性来更改,即 UpdateMode、ChildrenAsTriggers 和 Triggers。

特别是,UpdateMode 可决定 UpdatePanel 是设置为无条件刷新(默认情况)还是在特定条件下刷新。如果您希望有条件刷新(优化可更新面板的关键设置),请先将 UpdateMode 设置为 Conditional:

< asp:UpdatePanel id=UpdatePanel1 runat="server" UpdateMode="Conditional">

但是,请注意,有条件更新仅在页面上有多个面板时才适合。

某些情况下,您会发现很难将页面拆分为一组规范化的面板。在这些情况下,有一项技术可以帮助您防止子项触发回发。 ChildrenAsTriggers 属性是一个布尔属性(默认值为 true),它决定面板的子控件是否充当 AJAX 回发事件的触发器。在以下代码段中,Button1 控件不会引起面板刷新:

< asp:UpdatePanel id=UpdatePanel1 runat="server" ChildrenAsTriggers="false">
< CONTENTTEMPLATE>
< asp:Button id=Button1 onclick=Button1_Click runat="server" Text="Update">
< asp:Label id=Label1 runat="server">
< /CONTENTTEMPLATE>
< /asp:UpdatePanel>

在如此配置的面板中单击子按钮时,AJAX 回发仍然会执行与该按钮关联的回发代码,例如 Button1_Click 方法,但不会返回标记以刷新面板。

与有条件刷新相关的第三个属性是 Triggers。这是一个集合属性,列出了支配面板刷新的控件事件,如下所示:

< asp:UpdatePanel id=UpdatePanel1 runat="server">
< CONTENTTEMPLATE>
< asp:Label id=Label1 runat="server">
< /CONTENTTEMPLATE>
< TRIGGERS>
< asp:AsyncPostBackTrigger ControlID="Button1" EventName="Click">
< /TRIGGERS>
< /asp:UpdatePanel>

在本例中,Button1 的 Click 事件将刷新该面板。Button1 控件可以放在页面的任何位置。请注意,我谈论的是服务器事件。换言之,如果您要在用户更改下拉列表的选择时刷新面板,则必须先将 AutoPostBack 属性添加到该下拉列表。

AJAX 回发期间服务器上发生状态更改事件时,您仍可以配置一个面板进行刷新。请考虑以下代码:

< asp:TextBox id=TextBox1 runat="server">
< asp:UpdatePanel id=UpdatePanel1 runat="server">
< CONTENTTEMPLATE>
< asp:Label id=Label1 runat="server">
< /CONTENTTEMPLATE>
< TRIGGERS>
< asp:AsyncPostBackTrigger ControlID="TextBox1" EventName="TextChanged">
< /TRIGGERS>
< /asp:UpdatePanel>

每当 TextBox1 的内容更改时,标签都会被刷新:

Sub TextBox1_TextChanged(ByVal sender As Object, ByVal e As System.EventArgs) _
Handles TextBox1.TextChanged
Label1.Text = TextBox1.Text
End Sub

TextChanged 事件以及其他状态更改事件,都是由回发触发的服务器端事件。该页面仍需要按钮或自动回发控件来触发回发,以便更新标签。

图7 中的图表总结了将优化技术应用于可更新面板可能带来的好处。使用部分呈现,请求数据包的大小会稍微有所增加,但即使是优化最少的情况下,您得到的响应也比传统 ASP.NET 方案要小。而且毫无疑问,用户不会感觉到闪烁。

AJAX 应用程序体系结构3_第1张图片

图7 优化可更新面板的好处

但是,需要提出警告的是,具有大量视图状态的页面不会从部分呈现中获益很多。您可以节省 5KB 的标记,但如果您需要 30KB 视图状态的情况,则无济于事。

但在某些情况下,视图状态仅仅是由其他原因引起的问题的无辜替罪羊。假设 ASP.NET 页面中有一个日历控件,您使用可更新面板使刷新保持流畅:

< asp:UpdatePanel id=UpdatePanel1 runat="server">
< CONTENTTEMPLATE>
< asp:Calendar id=Calendar1 runat="server">
< /CONTENTTEMPLATE>
< /asp:UpdatePanel>

此片段的标记不超过 7KB,如果您添加一些样式,则可能增加到 10KB。现在,如果您禁用了该日历的视图状态,可节省大约 1KB 的数据,而这仅仅是总大小的 10%。这是因为,日历就标记而言是一个较大的控件。视图状态对它的影响微乎其微。ASP.NET AJAX 页面不应使用日历控件来实现日期选择功能。实际上,AJAX Control Toolkit 提供了一个扩展程序来代替,即 CalendarExtender 控件,当将其添加到普通文本框时,便可让您选择日期,有效地节省了 10KB 的标记。

每当文本框获得输入焦点时,此代码都会弹出一个日历:

< asp:textbox id=TextBox1 runat="server">
< ?xml:namespace prefix = act />
< asp:UpdatePanel id=UpdatePanel1 runat="server" UpdateMode="Conditional">
< CONTENTTEMPLATE>
< asp:label id=Label1 runat="server">
< /CONTENTTEMPLATE>
< /asp:UpdatePanel>

该日历完全在客户端创建,只需下载几个小图标即可。该扩展程序会自动将选定日期写入对应的文本框。但是,您可以使用一些 JavaScript 代码来处理客户端日期选择事件和执行所需操作。

评价部分呈现

部分呈现是使用 AJAX 功能提升 ASP.NET 网站最快捷的方式。它既不需要新的技术,也不需要新的体系结构,相反,它适合逐步增强现有网站。但是与纯 AJAX 方式(对远程服务的直接客户端调用)相比,部分呈现的性能受到很大影响。

根据实际经验,我认为部分呈现是构建坚如磐石的网站首个 AJAX 版本的最佳方式。之后,您可以逐步从传统 ASP.NET 体系结构迁移,并开始构建后端服务和丰富的表示层。在“领先技术”安装的下一部分,我将深入探讨 AJAX 的远程服务方式。从体系结构上来说,这的确很棒,但它可能需要对应用程序进行全面的重新设计,并且会带来一些新的问题。

Dino Esposito是 Solid Quality Learning 的顾问,并且是《Programming Microsoft ASP.NET 2.0》(Microsoft ASP.NET 2.0 编程)(Microsoft Press,2005)一书的作者。Dino 定居于意大利,经常在世界各地的业内活动中发表演讲。若要与 Dino 联系,请发送电子邮件至 [email protected],或访问他在 weblogs.asp.net/despos 上开设的博客。

你可能感兴趣的:(Ajax)