天天看點

ASP.Net WebForm溫故知新學習筆記:二、ViewState與UpdatePanel探秘

ASP.Net WebForm溫故知新學習筆記:二、ViewState與UpdatePanel探秘

經曆了上一篇《aspx與伺服器控件探秘》後,我們了解了aspx和伺服器控件背後的故事。這篇我們開始走進WebForm狀态保持的一大法寶—ViewState,對其刨根究底一下。然後,再對曾經很流行的ASP.Net AJAX方案中的利器—UpdatePanel這個神奇的區域一探究竟。

開篇:經曆了上一篇《aspx與伺服器控件探秘》後,我們了解了aspx和伺服器控件背後的故事。這篇我們開始走進WebForm狀态保持的一大法寶—ViewState,對其刨根究底一下。然後,再對曾經很流行的ASP.Net AJAX方案中的利器—UpdatePanel這個神奇的區域一探究竟。

一、隐藏的狀态—ViewState探秘

1.1 從Http的無狀态說起

  Http是一個無狀态協定,同一個會話的連續兩個請求互相不了解,它們由最新執行個體化的環境進行解析,除了應用本身可能已經存儲在全局對象中的所有資訊外,該環境不儲存與會話有關的任何資訊。另外,因為,浏覽器和伺服器之間是通過Socket進行通信,Http請求通常請求完畢就會關閉Socket連接配接,是以Http協定不會保持連接配接。如果保持連接配接會降低用戶端并發處理請求數,不保持連接配接又會降低處理速度(建立連接配接所需的時間會長一點);

PS:這裡我們可以這樣來了解:假如我們去一個大型商場購物購買某個産品,第一次去的時候是A銷售員接待了我們,帶領我們來到XX産品的櫃台并為我們推薦了XX産品;等我們回去使用XX産品後,覺得XX産品真心不錯。第二次我們又去,但是這次卻找不到上次那個A銷售員了,相反商場配置設定了另一個B銷售員來接待我們,他不知道我們上次選擇了XX産品,相反它卻一個勁地向我們推薦YY産品并把我們帶向YY産品的櫃台;這個時候,我們一般會說:我擦,把上次那個妹子給我叫來!

  基于Http協定的無狀态特性,我們在ASP.Net的開發中也會經常碰到這種情況:使用者上一次送出的東西,下次再送出時伺服器就不記得了。很多時候,我們感到很不解?後來,我們發現原來每一次的請求伺服器都開啟了不同的線程來處理,也就是說每次都會new一個XXX.aspx.cs中的類對象執行個體來進行處理(上一次new出來為我們處理的page對象也許早就被伺服器銷毀了)。比如,我們在xxx.aspx.cs代碼中寫入了一個int類型的number成員(初始為0),每次請求我們都想讓這個number自增一下,然後重新傳回給浏覽器。但就是這麼一個簡單的夢想,我們卻無法輕易的實作。

ASP.Net WebForm溫故知新學習筆記:二、ViewState與UpdatePanel探秘

  那麼,到底怎麼來破呢?大神們已經為我們想好了政策,我們可以使用隐藏域字段、Cookie、Session等來儲存狀态。而偉大的Microsoft還在ASP.Net中幫我們封裝了ViewState,以至于我們在WebForm中進行PostBack操作時,都感覺不到伺服器是無狀态的。

1.2 青春四處綻放—無處不在的ViewState

  (1)類似于Dictionary的一種資料結構

  如果你曾經使用過Dictionary或者Session的話,那麼你應該了解這種Key/Value的資料結構。這裡并沒有什麼高深的理論,ViewState通過String類型的資料作為索引。ViewState對應項中的值可以存儲任何類型的值(參數是Object類型),實施上任何類型的值存儲到ViewState中都會被裝箱為Object類型。

  例如,這裡我們可以改寫上面那個按鈕事件中的代碼:

ASP.Net WebForm溫故知新學習筆記:二、ViewState與UpdatePanel探秘
ASP.Net WebForm溫故知新學習筆記:二、ViewState與UpdatePanel探秘
1 protected void btnGetNumber_Click(object sender, EventArgs e)
 2 {
 3     //number++;
 4     //this.lblNumber.Text = number.ToString();
 5 
 6     object age = this.ViewState["age"];
 7     if (age == null)
 8     {
 9         age = 1;
10     }
11     else
12     {
13         age = Convert.ToInt32(age) + 1;
14     }
15     this.ViewState["age"] = age;
16     this.lblNumber.Text = age.ToString();
17 }      

View Code

   這裡,我們借助ViewState存儲了age的狀态值,第一次來我給你傳回1,後面再來我就加1再傳回給你。于是,在上一節我們所提到的那個問題(無法記住上次的number值,每次都傳回1)就解決了。

PS:ViewState不能存儲所有的資料類型,僅支援以下的這幾種:

String、Integer、Boolean、Array、ArrayList、Hashtable以及一些自定義類型

  我們都知道,Dictionary和Session都是存儲在伺服器端的。那麼,我們不禁要問,既然我們在伺服器端給ViewState增加了一個Key/Value對,并傳回給浏覽器端,ViewState又是存儲在什麼位置的呢?

  (2)大隐隐于市的“頁面級”隐藏字段

  跟Session和Dictionary的存儲位置不同,ViewState的作用域是頁面,也就是說ViewState是存儲在浏覽器的頁面之中的(這裡相比Session等,耗費的伺服器資源較少,也算是ViewState的優點之一吧),當你關閉某個aspx檔案後,那麼屬于這個aspx的ViewState也就不存在了。或許,這麼說來,我們還不是很了解,現在我們來實地看看。

ASP.Net WebForm溫故知新學習筆記:二、ViewState與UpdatePanel探秘

  ①首先,如果頁面上有一個runat="server"的form,當使用者請求這個頁面時,伺服器會自動添加一個_ViewState的隐藏域傳回給浏覽器。但是,我們發現這個ViewState的value看起來像一串亂碼?這是為什麼呢?這是因為伺服器在向浏覽器傳回html之前,對ViewState中的内容進行了Base64的加密編碼;

  ②其次,當使用者點選頁面中的某個按鈕送出表單時,浏覽器會将這個_VIEWSTATE的隐藏域也一起送出到服務端;伺服器端在解析請求時,會将浏覽器送出過來的ViewState進行反序列化後填充到ViewState屬性中(比如下圖中,我們可以通過一個軟體将_VIEWSTATE解碼得到一個如下圖所示的樹形結構);再根據業務處理需要,從這個屬性中根據索引找到具體的Value值并對其進行操作;操作完成後,再将ViewState進行Base64編碼再次傳回給浏覽器端;

ASP.Net WebForm溫故知新學習筆記:二、ViewState與UpdatePanel探秘

  ③是以,我們可以得出一個結論:VIEWSTATE适用于同一個頁面在不關閉的情況下多次與伺服器互動(PostBack)。這裡我們也可以通過下圖來溫習一下ViewState的流程,ViewState存放着“事故現場”,下次可以友善地“還原現場”,将無狀态的Http模拟成了有狀态的,也讓廣大的初學者了解不到無狀态的這個特性。

ASP.Net WebForm溫故知新學習筆記:二、ViewState與UpdatePanel探秘

1.3 喜歡就會放肆—又愛又恨的ViewState!

  事實上,除了我們手動在伺服器端向ViewState屬性中添加的K/V對資料,我們在aspx.cs代碼中為某些伺服器控件設定的值(例如:為Repeater設定DataSource中存入的資料集、為Label所設定的Text内容等,但不包括:TextBox、CheckBox、CheckboxList、RadioButtonList)都存入了ViewState中。這樣做的話,我們下次再向伺服器送出請求時,現有表單中所有的伺服器控件狀态都會記錄在ViewState中送出到伺服器,在伺服器端可以友善地對這些伺服器控件進行有狀态的操作并傳回,這無疑是讓我們歡喜的,因為友善了我們的開發過程,提高了我們的開發效率;

  但有人說:“喜歡就會放肆”,ViewState讓人又愛又恨啊。例如,在我們使用Repeater的過程中,WebForm會自動将DataSource(資料源,你可以了解為一個集合)存儲到ViewState中并傳回給浏覽器。可以參考下面的例子來實地了解一下:

  ①含有Repeater的aspx頁面:

ASP.Net WebForm溫故知新學習筆記:二、ViewState與UpdatePanel探秘
ASP.Net WebForm溫故知新學習筆記:二、ViewState與UpdatePanel探秘
1     <form id="form1" runat="server">
 2         <div align="center">
 3             <table class="test">
 4                 <tr class="first">
 5                     <td>
 6                         ID
 7                     </td>
 8                     <td>
 9                         産品名稱
10                     </td>
11                     <td>
12                         産品描述
13                     </td>
14                     <td>
15                         删除
16                     </td>
17                 </tr>
18                 <asp:Repeater ID="repeaterProducts" runat="server">
19                     <ItemTemplate>
20                         <tr>
21                             <td>
22                                 <%#Eval("Id") %>
23                             </td>
24                             <td>
25                                 <%#Eval("Name") %>
26                             </td>
27                             <td>
28                                 <%#Eval("Msg") %>
29                             </td>
30                             <td>
31                                 <a href='Product.ashx?Action=Delete&Id=<%#Eval("Id") %>'>删除</a>
32                             </td>
33                         </tr>
34                     </ItemTemplate>
35                 </asp:Repeater>
36             </table>
37         </div>
38     </form>      

  ②背景代碼模拟從資料庫中取得資料集合并綁定到Repeater中:

ASP.Net WebForm溫故知新學習筆記:二、ViewState與UpdatePanel探秘
ASP.Net WebForm溫故知新學習筆記:二、ViewState與UpdatePanel探秘
1 protected void Page_Load(object sender, EventArgs e)
 2 {
 3     if (!IsPostBack)
 4     {
 5         this.repeaterProducts.DataSource = this.GetProductList();
 6         this.repeaterProducts.DataBind();
 7     }
 8 }
 9 
10 private IList<Product> GetProductList()
11 {
12     IList<Product> productList = new List<Product>();
13     productList.Add(new Product() { Id = 1, Name = "康師傅友善面", Msg = "就是這個味兒!" });
14     productList.Add(new Product() { Id = 2, Name = "統一友善面", Msg = "還是那個味兒!" });
15     productList.Add(new Product() { Id = 3, Name = "白象友善面", Msg = "大骨濃湯啊!" });
16     productList.Add(new Product() { Id = 4, Name = "日本友善面", Msg = "不隻是愛情動作片!" });
17     productList.Add(new Product() { Id = 5, Name = "台灣友善面", Msg = "馬英九誇我好吃!" });
18 
19     return productList;
20 }          

  編譯生成後,通過檢視此頁面的html代碼,可以明顯看到一長串的_VIEWSTATE隐藏域。将此_VIEWSTATE複制到ViewStateDecoder中進行反編碼,可以發現它确實存儲了Repeater中的資料集合。這裡我們不禁要問:展示資料既然已經渲染成了html,為何還要存儲在ViewState隐藏域中?如果我們的資料集合是一百行、一千行資料的話,那ViewState隐藏域豈不很大(100k?200k?)?但不幸的是,這是ViewState的設計機制,要想依靠它來保持狀态,它就會将伺服器控件的狀态包括資料集合都存儲到其中,在浏覽器和伺服器之間來回傳遞保持狀态。

ASP.Net WebForm溫故知新學習筆記:二、ViewState與UpdatePanel探秘
ASP.Net WebForm溫故知新學習筆記:二、ViewState與UpdatePanel探秘

  這裡就涉及到網站的性能問題的探讨了:由于ViewState存儲在頁本身,是以如果存儲較大的值,使用者請求顯示頁面的速度會減慢(這對于網際網路系統來說,就是一個噩夢。你會選擇一個1秒内響應的網站浏覽還是5秒内響應的網站?)。又因為ViewState會随同Form表單一同回傳給伺服器,如果ViewState很大的話,Http封包也會很大,網站流量消耗也會增大。

  那麼,有沒有一種方法可以讓ViewState克制一下呢?别急,請看下面的介紹。

1.4 但愛就是克制—禁用還是不禁用ViewState?

  剛剛說到,因為ViewState會一定程度上影響性能,是以我們可以在不需要的時候禁用 ViewState。預設情況下 ViewState 将被啟用,并且是由每個控件(而非頁面開發人員)來決定存儲在 ViewState 中的内容。有時,這一資訊對應用程式并沒有什麼用處(例如上面提到的Repeater的資料集合,已經渲染生成了html顯示,還存儲了一份副本在ViewState裡邊)。盡管也沒什麼害處,但卻會明顯增加發送到浏覽器的頁面的大小。是以如果不需要用ViewState,最好還是将它關閉,特别是當 ViewState 很大的時候。當然,ViewState幫我們實作了某些伺服器控件狀态保持,是以在非必需的情況下,還是可以适度使用的,特别是在開發企業内部資訊系統的場景。

  那麼,怎樣來禁用ViewState呢?禁用ViewState又有什麼政策呢?下面我們一一來探讨。

  ①頁面級禁用ViewState:在aspx的首部的Page指令集中添加EnableViewState="false",該頁面中所有控件的狀态都不會存入ViewState的,頁面一下就會清爽許多;

<%@ Page Language="C#" AutoEventWireup="true" CodeBehind="RepeaterViewState.aspx.cs"
    Inherits="WebFormDemo.RepeaterViewState" EnableViewState="false" %>      

  禁用後,再次檢視生成的html代碼,我們會發現:咦,_VIEWSTATE還在那兒,但是明顯比先前的體積小了不少!

ASP.Net WebForm溫故知新學習筆記:二、ViewState與UpdatePanel探秘

  再将這個瘦身後的_VIEWSTATE複制到ViewStateDecoder中進行反編碼檢視,我們會發現,隻儲存了一個最基本的資訊,Repeater的那些資料集合沒有存入進去了。

ASP.Net WebForm溫故知新學習筆記:二、ViewState與UpdatePanel探秘

PS:為什麼禁用ViewState之後,頁面源代碼中仍然有_VIEWSTATE的隐藏域?

這是因為就算禁用了viewstate,aspx頁面中還是會有一個伺服器控件在那裡使用,這就是<form runat="server">。這時,如果你将form去掉runat="server",将其變為普通html标簽,那麼頁面就幹淨了,從此_VIEWSTATE這個隐藏域徹底消失在你的頁面中。  

  ②控件級禁用ViewState:在某些場景中,我們隻希望禁用某個控件(例如Repater)的ViewState,其他控件仍然通過ViewState保持狀态。這時,我們可以給指定的控件設定一個屬性EnableViewState="false"即可;

<asp:Repeater ID="repeaterProducts" runat="server" EnableViewState="false">
</asp:Repeater>      

  ③全局級禁用ViewState:園子裡的大神老趙(Jeffrey Zhao)曾經說過,“我如果建立一個WebForm項目,做的第一件事情就是去Web.config中将enableViewState設定為false進而将ViewState全局關閉”。那麼,我們如果希望将網站中所有頁面的ViewState都禁用,總不可能去一個一個頁面得修改Page指令吧?ASP.Net為我們提供了一個配置,我們隻需要在Web.config的system.web中增加一句配置即可:

<pages enableViewState="false" />      
PS:開發中也可以采用大神老趙的做法,先禁用,再選擇性啟用,畢竟沒有非要ViewState才能幹成的事兒!

  ④真正的禁用ViewState:剛剛我們的三種方法實踐後,在頁面還是出現_VIEWSTATE的隐藏域,盡管它保留了最基本的資訊。那麼,我們可能會問?怎樣才能徹底地真正地禁用ViewState,根本就别給我生成_VIEWSTATE的隐藏域。答案是有的,将<form runat="server"/>的runat="server"去掉,就不會出現了,但那樣又會偏離WebForm的開發模式,大部分的伺服器控件都無法正常使用,開發效率又會有所損失。

  綜上所述,在實際開發中應該權衡利弊,特殊情況特殊分析(到底這個場景該不該禁用ViewState),選擇是否禁用ViewState,采用何種方式禁用ViewState。對于ViewState的探秘本篇就到此為止,由于我本人了解的也不是很深刻,是以希望各位園友如果有了解,可以回複出來大家探讨共同進步。

二、飛來的利器—UpdatePanel探秘

2.1 從一個簡單四則運算電腦說起

  假如有以下一個場景,我們要做一個簡單的四則電腦。aspx頁面代碼和後端邏輯代碼如下:

  (1)aspx頁面代碼

ASP.Net WebForm溫故知新學習筆記:二、ViewState與UpdatePanel探秘
ASP.Net WebForm溫故知新學習筆記:二、ViewState與UpdatePanel探秘
<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
    <title>AJAX電腦</title>
</head>
<body>
    <form id="form1" runat="server">
    <div align="center">
        <asp:TextBox ID="txtNumber1" runat="server"></asp:TextBox>
        <asp:DropDownList ID="ddlFunc" runat="server">
            <asp:ListItem Value="0">+</asp:ListItem>
            <asp:ListItem Value="1">-</asp:ListItem>
            <asp:ListItem Value="2">*</asp:ListItem>
            <asp:ListItem Value="3">/</asp:ListItem>
        </asp:DropDownList>
        <asp:TextBox ID="txtNumber2" runat="server"></asp:TextBox>
        <asp:Button ID="btnGetResult" runat="server" Text="=" Width="50" 
            onclick="btnGetResult_Click" />
        <asp:Label ID="lblResult" runat="server" Text="" Font-Bold="true"></asp:Label>
    </div>
    </form>
</body>
</html>      

  (2)後置邏輯代碼

ASP.Net WebForm溫故知新學習筆記:二、ViewState與UpdatePanel探秘
ASP.Net WebForm溫故知新學習筆記:二、ViewState與UpdatePanel探秘
public partial class AjaxCalculator : System.Web.UI.Page
{
        protected void Page_Load(object sender, EventArgs e)
        {

        }

        protected void btnGetResult_Click(object sender, EventArgs e)
        {
            int number1 = Convert.ToInt32(this.txtNumber1.Text);
            int number2 = Convert.ToInt32(this.txtNumber2.Text);
            int result = 0;
            switch(this.ddlFunc.SelectedValue)
            {
                case "0":
                    result = number1 + number2;
                    break;
                case "1":
                    result = number1 - number2;
                    break;
                case "2":
                    result = number1 * number2;
                    break;
                case "3":
                    if(number2 == 0)
                    {
                        throw new DivideByZeroException("除數不能為0!");
                    }
                    result = number1 / number2;
                    break;
            }
            this.lblResult.Text = result.ToString();
        }
}      

  生成後運作該頁面,可以達到以下的效果。我們輸入兩個數字後,選擇是加法、減法、還是乘除法後,點選=按鈕,即可重新整理頁面顯示運算結果。

ASP.Net WebForm溫故知新學習筆記:二、ViewState與UpdatePanel探秘

  在WebForm中,每一次點選runat="server"的按鈕都會将調用form.submit将請求送出到伺服器,伺服器會傳回新的頁面html進行頁面重繪。這是一個整頁的重新整理操作,不符合AJAX的風格需求。是以,我們想要将其改為AJAX版本的,除了使用基本的XMLHttpRequest外,我們還可以使用基于JQuery的AJAX方案,這些都是輕量級的原生态的AJAX技術方案。但我們偉大的微軟(我哭啊,真是為我們考慮啊,連AJAX方案都為我們解決了,而且還提供了AJAX控件供我們使用,我們拖控件的習慣可以用到AJAX方案上了!!!)還為我們提供了一套叫做ASP.Net AJAX的技術方案,通過這套方案,我們可以在ASP.Net很容易地實作AJAX效果,甚至都不需要我們懂JavaScript。是以,也就出現了前些年,很多WebForm開發者陸續使用ASP.Net AJAX Extension進行AJAX開發,紛紛表示:AJAX如此簡單,我等豈能不會?但是,雖然它簡單易行,由于其性能問題一直被人诟病,而我們這些菜鳥也未能了解其性能問題的原因,本着知其然也知其是以然的目标,現在我們來使用它并剖析它一下。

2.2 天上掉下個林妹妹—使用UpdatePanel控件

  不得不說,UpdatePanel真的是天上掉下的林妹妹,一個神奇的控件!有了它,我們可以将頁面中需要進行局部重新整理的内容放到其ContentTemplate中,一個需要整頁重新整理的操作便可以成為局部重新整理。現在,我們首先來使用其改造剛剛的簡單四則電腦頁面。

  (1)加入UpdatePanel,并将電腦html内容拖入ContentTemplate中

<form id="form1" runat="server">
    <div align="center">
        <asp:ScriptManager ID="scriptManager" runat="server">
        </asp:ScriptManager>
        <asp:UpdatePanel ID="updatePanel" runat="server">
            <ContentTemplate>
                <asp:TextBox ID="txtNumber1" runat="server"></asp:TextBox>
                <asp:DropDownList ID="ddlFunc" runat="server">
                    <asp:ListItem Value="0">+</asp:ListItem>
                    <asp:ListItem Value="1">-</asp:ListItem>
                    <asp:ListItem Value="2">*</asp:ListItem>
                    <asp:ListItem Value="3">/</asp:ListItem>
                </asp:DropDownList>
                <asp:TextBox ID="txtNumber2" runat="server"></asp:TextBox>
                <asp:Button ID="btnGetResult" runat="server" Text="=" Width="50" OnClick="btnGetResult_Click" />
                <asp:Label ID="lblResult" runat="server" Text="" Font-Bold="true"></asp:Label>
            </ContentTemplate>
        </asp:UpdatePanel>
    </div>
</form>      

  (2)運作該頁面,通過開發人員工具檢視Http請求

ASP.Net WebForm溫故知新學習筆記:二、ViewState與UpdatePanel探秘

  通過檢視請求封包,我們了解到此次的請求響應不再是傳回整頁的html内容,而隻是我們放在了UpdatePanel裡面的html内容,頁面也沒有再重新整理,于是不禁感歎一句:AJAX,So easy!媽媽再也不用擔心我的頁面了!

2.3 直到看見XmlHttpRequest才是唯一的答案—UpdatePanel原來如此

  正當我們沉浸在UpdatePanel為我們提供的神奇的AJAX世界裡時,我們不禁對UpdatePanel為我們做了哪些工作産生了興趣。

  (1)首先,我們知道AJAX的核心對象是XmlHttpRequest,那麼原生态的AJAX請求的JS方法是如何寫的呢?

function ajax(url, onsuccess) {
    var xmlhttp = window.XMLHttpRequest ? new XMLHttpRequest() : new ActiveXObject('Microsoft.XMLHTTP'); //建立XMLHTTP對象,考慮相容性。XHR
    xmlhttp.open("POST", url, true); //“準備”向伺服器的xx.ashx發出Post請求(GET可能會有緩存問題)。這裡還沒有送出請求

    //AJAX是異步的,并不是等到伺服器端傳回才繼續執行
    xmlhttp.onreadystatechange = function () {
        if (xmlhttp.readyState == 4) //readyState == 4 表示伺服器傳回完成資料了。之前可能會經曆2(請求已發送,正在進行中)、3(響應中已有部分資料可用了,但是伺服器還沒有完成響應的生成)
        {
            if (xmlhttp.status == 200) //如果Http狀态碼為200則是成功
            {
                onsuccess(xmlhttp.responseText);
            }
            else {
                alert("AJAX伺服器傳回錯誤!");
            }
        }
    }
    //不要以為if (xmlhttp.readyState == 4) {在send之前執行!!!!
    xmlhttp.send(); //這時才開始發送請求。并不等于伺服器端傳回。請求發出去了,我不等!去監聽onreadystatechange吧!
}      

  (2)其次,通過檢視運作頁面的html,我們可以發現加入UpdatePanel後,我們的html中多了這麼幾個js引用。

ASP.Net WebForm溫故知新學習筆記:二、ViewState與UpdatePanel探秘

  (3)既然我們知道要發AJAX請求,必然會涉及到XmlHttpRequest。那麼,我們就在這幾個js中取看看是否有涉及到XmlHttpRequest。通過檢視,我們找到了這樣一個似曾相識的js方法:

function Sys$Net$XMLHttpExecutor$executeRequest() {
        /// <summary locid="M:J#Sys.Net.XMLHttpExecutor.executeRequest" />
        if (arguments.length !== 0) throw Error.parameterCount();
        this._webRequest = this.get_webRequest();
        if (this._started) {
            throw Error.invalidOperation(String.format(Sys.Res.cannotCallOnceStarted, 'executeRequest'));
        }
        if (this._webRequest === null) {
            throw Error.invalidOperation(Sys.Res.nullWebRequest);
        }
        var body = this._webRequest.get_body();
        var headers = this._webRequest.get_headers();
        this._xmlHttpRequest = new XMLHttpRequest();
        this._xmlHttpRequest.onreadystatechange = this._onReadyStateChange;
        var verb = this._webRequest.get_httpVerb();
        this._xmlHttpRequest.open(verb, this._webRequest.getResolvedUrl(), true );
        this._xmlHttpRequest.setRequestHeader("X-Requested-With", "XMLHttpRequest");
        if (headers) {
            for (var header in headers) {
                var val = headers[header];
                if (typeof(val) !== "function")
                    this._xmlHttpRequest.setRequestHeader(header, val);
            }
        }
        if (verb.toLowerCase() === "post") {
            if ((headers === null) || !headers['Content-Type']) {
                this._xmlHttpRequest.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded; charset=utf-8');
            }
            if (!body) {
                body = "";
            }
        }
        var timeout = this._webRequest.get_timeout();
        if (timeout > 0) {
            this._timer = window.setTimeout(Function.createDelegate(this, this._onTimeout), timeout);
        }
        this._xmlHttpRequest.send(body);
        this._started = true;
}      

  由以上的方法名我們可以猜到,此方法是一個執行AJAX請求的方法。在此方法中,建立了XmlHttpRequest對象,也使用了open方法指明以GET還是POST方法向伺服器哪個處理程式發送請求,并且也為該請求指定了請求成功後需要執行的回調函數方法(onreadystatechange),最後調用send方法正式發送請求

  由此,我們可以初步分析出一個結論:UpdatePanel本質還是幫我們封裝了以XmlHttpRequest為核心的一系列方法幫我們将CodeBehind中的同步事件變為了異步操作,并通過DOM更新指定的HTML内容,使得我們可以友善地實作AJAX效果。

  但是,我們也不由發出感歎:本來可以很簡單地使用XmlHttpRequest來實作的東西,為什麼使用UpdatePanel會引入這麼多js,并且為我們傳回的東西還是那麼多(比如上面的例子,我隻需要的資料是一個結果,卻給我傳回一部分無用的html,還有一系列的hiddenId之類的資料)。在對性能要求較高的應用場合,如果使用UpdatePanel來實作AJAX會增加伺服器的負載,并且會消耗掉不必要的網絡流量(比如每次請求都會來回都會發送ViewState裡的資料,在性能和資料量上都會造成損失)。園子裡的浪子曾經在他的博文《遠離UpdatePanel帶給我的噩夢》裡邊寫到:“UpdatePanel在頁面小的時候還是很好用的,而當頁面控件數不斷上升的時候,UpdatePanel就開始直線下降,我們現在頁面有4,5百個控件,每做一次PostBack需要長達15秒鐘之長,實在讓人無法忍受。”

  那麼,有木有方式可以替換UpdatePanel呢?其實答案很簡單,那就是使用基于XmlHttpRequest的js方法,再加上一定的js回調函數即可。這就要求我們掌握javascript,不能隻做拖UpdatePanel控件的程式員。現在基于js的JQuery庫也早已為我們封裝了XmlHttpRequest,提供了ajax開發的一系列方法供我們調用,相當于UpdatePanel的“重量級”來說,可謂是輕了不少,是一個“輕量級”的AJAX開發方式。通過借助jQuery Ajax+ashx可以友善地在.Net中進行Ajax開發,并且具有不錯的性能,這也是我實習所在的企業中經常用到的方式。

三、學習總結

  本篇主要學習了WebForm中的狀态保持法寶—ViewState,以及曾經的ASP.Net AJAX方案的利器—UpdatePanel,雖然一直在說這個不好,那個别用。但是,微軟之是以為我們提供了這些東西,肯定有它存在的理由,并不一定都是不好的東西。所謂利器在手,沒有一點内功心法的人還是使用不好它,無法發揮出其100%的優勢。是以,身為.Net學習者的我們,不能滿足于微軟為我們所提供的便利,要知其然也知其是以然,做一個上進的程式員,加油吧!

  校園招聘的大潮就快來臨,希望園子裡跟我一樣即将畢業的菜鳥們能夠好好複習基礎,在招聘中赢得一份好offer,實作自己的價值!

參考資料

  (1)楊中科,《特供2014版ASP.Net教程》,http://net.itcast.cn/subject/tegongnet/index.html

  (2)匿名未知,《狀态儲存機制之ViewState概述及應用》,http://www.jb51.net/article/33686.htm

  (3)popping_dancer,《ASP.Net原理篇之ViewState》,http://www.2cto.com/kf/201210/160413.html

  (4)玉開,《ASP.Net 4.0新特性-輸出更純淨的Html代碼》,http://www.cnblogs.com/yukaizhao/archive/2010/05/22/asp-net-new-feature-pure-html.html

  (5)Infinities Loop,《ASP.Net ViewState詳解》,http://www.cnblogs.com/wwan/archive/2010/11/18/1880357.html

  (6)LifelongLearning,《ASP.Net 4.0中ViewState使用簡介》,http://www.csharpwin.com/dotnetspace/13220r6527.shtml

  (7)自由飛,《禁用VIEWSTATE之後(一)》,http://www.cnblogs.com/freeflying/archive/2009/12/28/1634229.html

  (8)MSDN,《了解使用ASP.NET AJAX進行局部頁面重新整理》,http://msdn.microsoft.com/zh-cn/dd361854.aspx

  (9)xiaomin,《UpdatePanel工作原理》,http://www.cnblogs.com/xiaomin/archive/2011/11/01/2232215.html

  (10)浪子,《遠離UpdatePanel給我的噩夢》,http://www.cnblogs.com/walkingboy/archive/2007/01/09/615691.html

作者:周旭龍

出處:http://edisonchou.cnblogs.com

本文版權歸作者和部落格園共有,歡迎轉載,但未經作者同意必須保留此段聲明,且在文章頁面明顯位置給出原文連結。