Hello World
Spiga

讲座展示:TechEd Europe DEV 411 - AJAX Patterns with ASP.NET AJAX(3)

2006-12-13 03:30 by 老赵, 4333 visits

这次我选择的讲座内容,是最近在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>Nature</asp:ListItem>
            <asp:ListItem>Landscapes</asp:ListItem>
            <asp:ListItem>Cityscapes</asp:ListItem>
            <asp:ListItem>Conference Trips</asp:ListItem>
            <asp:ListItem>Microsoft</asp: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>Nature</asp:ListItem>
    <asp:ListItem>Landscapes</asp:ListItem>
    <asp:ListItem>Cityscapes</asp:ListItem>
    <asp:ListItem>Conference Trips</asp:ListItem>
    <asp:ListItem>Microsoft</asp: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)。

(未完待续)

Creative Commons License

本文基于署名 2.5 中国大陆许可协议发布,欢迎转载,演绎或用于商业目的,但是必须保留本文的署名赵劼(包含链接),具体操作方式可参考此处。如您有任何疑问或者授权方面的协商,请给我留言

Add your comment

11 条回复

  1. 老赵
    admin
    链接

    老赵 2006-12-13 03:31:00

    嗬,一半时间都没有到,这个讲座够长的,呵呵。

  2. 喜欢吹风的感觉
    *.*.*.*
    链接

    喜欢吹风的感觉 2006-12-13 07:17:00

    老赵产量就是高啊.收藏先:)

  3. 老赵
    admin
    链接

    老赵 2006-12-13 09:25:00

    @喜欢吹风的感觉
    还好,因为这些都不是“原创”,所以比较快。:)

  4. Blue Lotus
    *.*.*.*
    链接

    Blue Lotus 2006-12-13 13:14:00

    UpdatePanel设计很巧,但我觉得它更多是象给ASP.NET打一个AJAX的补丁。对于现有的ASP.NET应用,可以用 UpdatePanel来简单的添加AJAX的无刷新的功能,但是一个项目从头开始,是否有必要还是基于ASP.NET这种以postback为基础的模式(updatepanel应该算是异步的postback吧。),还是基于它的client framework来开发以client为中心的模式?我想听听你的意见。

    另外,GWT刚刚open source,GWT designer的推出也大大简化了GWT的开发。微软这次又落在了后面。

  5. 老赵
    admin
    链接

    老赵 2006-12-13 13:43:00

    @Blue Lotus
    我认为UpdatePanel这么做是必须的,否则就无法在服务器端构造一个完整的页面对象了,我们现在可以像普通的PostBack一样处理的原因就在于服务器端Life Cycle的一些步骤并不知道异步PostBack和普通PostBack有什么区别。使用UpdatePanel是个非常迅速为应用添加AJAX特性的方法,合理使用也是可以的。不过ASP.NET AJAX也有非常好用的Client Framework特性,不过就需要编写更多的JS代码,所以还是看个人习惯吧。ASP.NET AJAX对于Client为中心的开发和Server中心的开发都有很好的支持。
    我个人来说倒的确没有任何的倾向性,以哪个为中心就以哪个为中心,从运行效率、开发效率、安全性、可维护性能多个方面进行权衡。

  6. Blue Lotus
    *.*.*.*
    链接

    Blue Lotus 2006-12-13 14:15:00

    谢谢你的回答。

    就象AJAX Design Patterns中讲的一样,AJAX的应用分为豪华型的应用和轻量型的应用两种。前者如gmail, google doc, hotmail等,后者如del.icio.us,flickr。这些都是很好的应用,只是需求不同而已。但是对于真正的企业级应用来讲,尤其是从桌面应用转移到web应用,要求rich UI的应用来将,以client为中心是一个很自然的选择。就以window live来看,也基本上是以client为中心的应用。

  7. 老赵
    admin
    链接

    老赵 2006-12-13 14:24:00

    @Blue Lotus
    因为UpdatePanel事实上还比较难以处理Script真正复杂的情况,或者处理起来代价很高。而且使用UpdatePanel的话必然和服务器端的耦合度非常高,这也是权衡的一方面。

  8. 魏晋遗疯[未注册用户]
    *.*.*.*
    链接

    魏晋遗疯[未注册用户] 2006-12-14 14:38:00

    我昨天问了您一个问题,当一个UpdatePanel中的内容刷新的时候,其他的UpdatePanel中的DropDownlist会闪一下。您的解释是浏览器的对<Select>的刷新是通过先删除再重新建立,所以会闪。刚才又看了一下您的这篇文章,可以通过设置UpdatePanel的UpdateMode为Conditional来实现不跟随其他UpdatgePanel来刷新,我试了一下,确实可以。
    但现在的问题是为什么Ajax Beta2 要默认设置UpdateMode为Always,即一个页面上的UpdatePanel刷新是一起刷新的(不知我理解的对不对),设置为Always的应用的场合有哪些呢?

  9. 老赵
    admin
    链接

    老赵 2006-12-14 14:45:00

    @魏晋遗疯
    其实我对于您的问题的答案就是“尝试尽量不要让DropDownList更新”,既然现在能避免了那么就再好不过了。
    其实UpdateMode默认为Always的想法是,保证了在默认情况下每个UpdatePanel都会更新吧,这样“看上去”至少该更新的都更新了。不过Best Pratise是“只更新需要更新的UpdatePanel”,这时候就要把UpdateMode设为Conditional。而UpdateMode为Always则表示每次异步PostBack,这个UpdatePanel都会更新。

  10. 魏晋遗疯[未注册用户]
    *.*.*.*
    链接

    魏晋遗疯[未注册用户] 2006-12-14 14:58:00

    知道了,谢谢你,真是播云见日阿。
    另外,如果您查看帖子的回复是通过打开自己发的帖子来做的,我觉得像Q&A那种会有超多回复的您看起来会很麻烦,有没有可能写个工具每隔一段时间抓一下帖子的最新信息?通过RSS什么的应该可以吧,或许这会减少您的工作量。

  11. 老赵
    admin
    链接

    老赵 2006-12-14 15:11:00

    @魏晋遗疯
    还好,博客园会把回复作为Email发送到我邮箱中,并提供了访问这个回复的地址,所以还算方便吧。
    RSS的话……似乎博客园没有提供这个功能?如果一定要的话只能请求HTML后自己分析了。:)

发表回复

登录 / 登录并记住我 ,登陆后便可删除或修改已发表的评论 (请注意保留评论内容)

昵称:(必填)

邮箱:(必填,仅用于Gavatar

主页:(可选)

评论内容(大于5个字符):

  1. Your Name yyyy-MM-dd HH:mm:ss

使用Live Messenger联系我