这次我选择的讲座内容,是最近在TechEd 2006 Europe中Andre Snanbria和Jeff Prosise的讲座“AJAX Pattern with ASP.NET AJAX”。Jeff Prosise是Wintellect的Co-Founder,Andre Sanabria是ASP.NET AJAX Team的Lead Program Manager。在MSDN's Showtime上已经有了这个讲座的完整视频,而我在早些时候给Andre Sanabria写了封Email,一星期后他给我寄来了这个讲座的PPT和Demo,大家可以点击这里下载。
这次讲座的主要内容是讲述了使用ASP.NET AJAX开发AJAX应用的最佳实践,在这次讲座里,会对建立轻量级的客户端控件的方法进行深入,讲述了如何优化脚本代码,并提出了如何避免AJAX开发中常见的问题。
本篇文章是这次讲座展示的第三篇,使用了一个例子来观察UpdatePanel的工作方式,并通过几个步骤对这个例子进行优化。
讲座内容
Andre:我先从展示没有UpdatePanel时的体验开始(~/Optimization/0.SimpleForm.aspx)。这是个简单的页面(上图左,请注意Back按钮无法使用),我们有几个相册供用户选择,然后我们将为它们添加Tag。现在我们没有使用UpdatePanel,当选择了某一项时,您会发现发生了一个完整的页面刷新(上图中,我把Back按钮可以使用了)。因为现在在我的本地机器上演示,因此这个刷新非常迅速。但是在互联网上就不会像本地那么快了,带宽会非常小。您可以发现,当我点击页面上每个单选按钮后,页面都会有完整的刷新(上图右,Back按钮的列表多了几项)。
Andre:我们现在来看一下该如何解决这个问题。我们解决这个问题的方法只是将UpdatePanel添加到页面上。正如Jeff刚才提到的那样,我们在刚才的应用里使用了许多UpdatePanel。现在这个也是展示UpdatePanel的不错的例子(~/Optimization/1.PartialRendering.aspx)。
Andre:我们首先先使用UpdatePanel包含一个服务器端的控件“Panel”(如下):
<asp:UpdatePanel runat="server" ID="contentUpdatePanel"> <ContentTemplate> <asp:Panel runat="server" ID="contentPanel" style="padding: 10px" /> ContentTemplate> <Triggers> <asp:AsyncPostBackTrigger ControlID="albumList" /> Triggers> asp:UpdatePanel>
Andre:然后我们使用UpdatePanel将一些CheckBox控件包含起来,我们那些Tag都是CheckBox(如下):
<asp:UpdatePanel runat="server" ID="tagsListUpdatePanel"> <ContentTemplate> <asp:CheckBoxList runat="server" ID="tagsList" AutoPostBack="TRUE" Enabled="false" RepeatColumns="3" OnSelectedIndexChanged="OnTagsSelectedIndexChanged"> <asp:ListItem>Natureasp:ListItem> <asp:ListItem>Landscapesasp:ListItem> <asp:ListItem>Cityscapesasp:ListItem> <asp:ListItem>Conference Tripsasp:ListItem> <asp:ListItem>Microsoftasp:ListItem> asp:CheckBoxList> ContentTemplate> asp:UpdatePanel>
Andre:我们这里还有一个UpdatePanel(如下):
<asp:UpdatePanel runat="server" ID="tagsUpdatePanel"> <ContentTemplate> <asp:Label runat="server" ID="tagsLabel" /> ContentTemplate> asp:UpdatePanel>
Andre:我们先来看一下现在的用户体验(老赵:这里就省略了),再来观察数据在客户端和服务器端之间是如何交换的。
Andre:我们先打开一个工具——Web Development Helper,然后打开Logging开关。当我们点击某个Tagging时,这个小工具记录了从我的浏览器发出的Request和Server回复的内容(上图左)。因此我们就可以清楚地看到当我们选中一个CheckBox时,一个新的Request就发送到服务器端了。那么我们现在就来仔细看一下到底在这里发生了什么(上图中)。
Andre:首先,一个请求放送到当前的页面。然后我们可以发现一个新的Header被添加到了请求中,它的名称是“x-microsoftajax”,值为“Delta=true”。这表示客户端告诉服务器端,现在的是一个异步的请求,需要对页面上的部分内容进行刷新。如果看一下部分刷新的实现的话,可以发现我们会查找Header,如果有上面的信息,则表示我们必须进行特别的步骤了,就是Jeff刚才提到的那些步骤。当然,这只是第一步。
Andre:第二步我想展示给大家看的则是到底发送了那些内容。大部分人没有意识到的是,当UpdatePanel在工作时,整张页面被发送到了服务器端。就像Jeff说过,我们会将我们需要的所有信息,例如ViewState发送到服务器端。我们来看一下发送的内容(上图右)。发送了ScriptManager,ViewState内容等等,这是所有被发送到服务器端的内容。
Andre:现在还有一个问题,服务器端到底返回了什么。我们可以从“Response Content”看到(上图)。这里有UpdatePanel的ID,我们也可以猜到,它返回了UpdatePanel里所有的HTML内容,在这里是个DIV,这就是我们会用来替换的内容。然后还有第二个UpdatePanel和第三个UpdatePanel。然后我们会将所有的ViewState发送到客户端。就像Jeff说过的,我们需要在Roundtrip时需要包含ViewState的原因是为了传输我们在客户端作了什么改变,在服务器端又作了什么改变。我们需要使用最新的数据来重建整个控件树,这就是我们需要ViewState数据的原因。
Andre:下面就是Jeff提到过的一个最佳实践。我打开这个工具的目的是要让大家知道,每次我们点击CheckBox的时候发生了什么(上图)。每次我们点击CheckBox时,我们就会去服务器端请求数据,然后将页面进行刷新。我们该如何解决这个问题?这个问题其实有个比较容易的解决方法。(Andre不小心关闭了Visual Studio)呃……这个解决方案并不是关闭Visual Studio,我们再来重新看一下(~/Optimization/2.OptimizeFrequency.aspx),我们在这里把CheckBoxList的AutoPostBack属性设为False(如下):
<asp:CheckBoxList runat="server" ID="tagsList" AutoPostBack="false" Enabled="false" RepeatColumns="3"> <asp:ListItem>Natureasp:ListItem> <asp:ListItem>Landscapesasp:ListItem> <asp:ListItem>Cityscapesasp:ListItem> <asp:ListItem>Conference Tripsasp:ListItem> <asp:ListItem>Microsoftasp:ListItem> asp:CheckBoxList>
Andre:这里的想法就是,我们不让每次CheckBox被选中或取消时就发送一次请求,我们只在我们准备好的情况下发送数据。我们再看一下页面(老赵:这里的截图就省略了),当我们选中一个像册时会发送一个请求。每次但我们点击CheckBox时,我们不会给服务器端发送数据。不过当我们点击Update按钮时,一个请求就出现了。这里的关键就在于,即使我们能够很快地与服务器端交互,我们也要仔细考虑一下,我们是否真的必要把AutoPoskBack打开,让控件自动地与服务器端交互,因为这意味着页面上所有的数据将向服务器端进行一个来回。
Andre:这里还有一些比较有趣的事情,我想展示一下刚才发生了什么。我们依旧回到刚才的“Response Content”,这就是从服务器端发回的内容,但是我们在这里使用“Partial Rendering”视图(上图)。这里发生的事情是,有个UpdatePanel被更新了,在每次请求之后,我们会更新所有的3个UpdatePanel,我们在这里使用UpdatePanel的默认设置,UpdateMode为Always。这样,无论向服务器端请求的内容是什么,所有的UpdatePanel都会同时进行刷新。这里的问题就在于,我们可能不需要重新生成所有的UpdatePanel,我们需要的是简单地选择几个需要更新的UpdatePanel。这里就使用到UpdatePanel的一个属性,就是“Conditional Rendering”——噢,对不起,是“UpdateMode”。让我来告诉您我到底在说什么(~/Optimization/3.OptimizeBandwidth.aspx)。
Andre:在这里我将UpdatePanel的UpdateMode属性设为了“Conditional”(老赵:这部分代码就不粘贴了)。现在的状况就变成了,UpdatePanel不必在每次请求时都进行更新,而是只有在特定情况下才重新生成它的内容。这些情况可能是我们通过UpdatePanel内部的控件进行了更新或者调用UpdatePanel的Update方法。第二种情况则是通过了UpdatePanel的Trigger,当一个控件被作为UpdatePanel的Trigger时,则表示当这个控件发起PostBack之后,UpdatePanel将被更新。
Andre:我们再来看一下,当我们点击Update按钮之后,可以发现Reponse的内容变得多小(上图左),当我们查看更新的UpdatePanel时,可以发现现在只有一个UpdatePanel被更新了(上图右)!从我们的最佳实践方面来说,我们不仅从数据传输着手,也考虑到了页面加载方面的问题。我们在这里将UpdatePanel的默认设置改成了条件生成(Conditional Rendering)。
(未完待续)