ASP.Net WebForm温故知新学习笔记:二、ViewState与UpdatePanel探秘
原文地址:http://www.cnblogs.com/edisonchou/p/3901559.html
?
開篇:經(jīng)歷了上一篇《aspx與服務(wù)器控件探秘》后,我們了解了aspx和服務(wù)器控件背后的故事。這篇我們開始走進WebForm狀態(tài)保持的一大法寶—ViewState,對其刨根究底一下。然后,再對曾經(jīng)很流行的ASP.Net AJAX方案中的利器—UpdatePanel這個神奇的區(qū)域一探究竟。
一、隱藏的狀態(tài)—ViewState探秘
1.1 從Http的無狀態(tài)說起
Http是一個無狀態(tài)協(xié)議,同一個會話的連續(xù)兩個請求互相不了解,它們由最新實例化的環(huán)境進行解析,除了應(yīng)用本身可能已經(jīng)存儲在全局對象中的所有信息外,該環(huán)境不保存與會話有關(guān)的任何信息。另外,因為,瀏覽器和服務(wù)器之間是通過Socket進行通信,Http請求通常請求完畢就會關(guān)閉Socket連接,因此Http協(xié)議不會保持連接。如果保持連接會降低客戶端并發(fā)處理請求數(shù),不保持連接又會降低處理速度(建立連接所需的時間會長一點);
PS:這里我們可以這樣來理解:假如我們?nèi)ヒ粋€大型商場購物購買某個產(chǎn)品,第一次去的時候是A銷售員接待了我們,帶領(lǐng)我們來到XX產(chǎn)品的柜臺并為我們推薦了XX產(chǎn)品;等我們回去使用XX產(chǎn)品后,覺得XX產(chǎn)品真心不錯。第二次我們又去,但是這次卻找不到上次那個A銷售員了,相反商場分配了另一個B銷售員來接待我們,他不知道我們上次選擇了XX產(chǎn)品,相反它卻一個勁地向我們推薦YY產(chǎn)品并把我們帶向YY產(chǎn)品的柜臺;這個時候,我們一般會說:我擦,把上次那個妹子給我叫來!
基于Http協(xié)議的無狀態(tài)特性,我們在ASP.Net的開發(fā)中也會經(jīng)常碰到這種情況:用戶上一次提交的東西,下次再提交時服務(wù)器就不記得了。很多時候,我們感到很不解?后來,我們發(fā)現(xiàn)原來每一次的請求服務(wù)器都開啟了不同的線程來處理,也就是說每次都會new一個XXX.aspx.cs中的類對象實例來進行處理(上一次new出來為我們處理的page對象也許早就被服務(wù)器銷毀了)。比如,我們在xxx.aspx.cs代碼中寫入了一個int類型的number成員(初始為0),每次請求我們都想讓這個number自增一下,然后重新返回給瀏覽器。但就是這么一個簡單的夢想,我們卻無法輕易的實現(xiàn)。
那么,到底怎么來破呢?大神們已經(jīng)為我們想好了策略,我們可以使用隱藏域字段、Cookie、Session等來保存狀態(tài)。而偉大的Microsoft還在ASP.Net中幫我們封裝了ViewState,以至于我們在WebForm中進行PostBack操作時,都感覺不到服務(wù)器是無狀態(tài)的。
1.2 青春四處綻放—無處不在的ViewState
(1)類似于Dictionary的一種數(shù)據(jù)結(jié)構(gòu)
如果你曾經(jīng)使用過Dictionary或者Session的話,那么你應(yīng)該了解這種Key/Value的數(shù)據(jù)結(jié)構(gòu)。這里并沒有什么高深的理論,ViewState通過String類型的數(shù)據(jù)作為索引。ViewState對應(yīng)項中的值可以存儲任何類型的值(參數(shù)是Object類型),實施上任何類型的值存儲到ViewState中都會被裝箱為Object類型。
例如,這里我們可以改寫上面那個按鈕事件中的代碼:
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的狀態(tài)值,第一次來我給你返回1,后面再來我就加1再返回給你。于是,在上一節(jié)我們所提到的那個問題(無法記住上次的number值,每次都返回1)就解決了。
PS:ViewState不能存儲所有的數(shù)據(jù)類型,僅支持以下的這幾種: String、Integer、Boolean、Array、ArrayList、Hashtable以及一些自定義類型
我們都知道,Dictionary和Session都是存儲在服務(wù)器端的。那么,我們不禁要問,既然我們在服務(wù)器端給ViewState增加了一個Key/Value對,并返回給瀏覽器端,ViewState又是存儲在什么位置的呢?
(2)大隱隱于市的“頁面級”隱藏字段
跟Session和Dictionary的存儲位置不同,ViewState的作用域是頁面,也就是說ViewState是存儲在瀏覽器的頁面之中的(這里相比Session等,耗費的服務(wù)器資源較少,也算是ViewState的優(yōu)點之一吧),當你關(guān)閉某個aspx文件后,那么屬于這個aspx的ViewState也就不存在了。或許,這么說來,我們還不是很了解,現(xiàn)在我們來實地看看。
①首先,如果頁面上有一個runat="server"的form,當用戶請求這個頁面時,服務(wù)器會自動添加一個_ViewState的隱藏域返回給瀏覽器。但是,我們發(fā)現(xiàn)這個ViewState的value看起來像一串亂碼?這是為什么呢?這是因為服務(wù)器在向瀏覽器返回html之前,對ViewState中的內(nèi)容進行了Base64的加密編碼;
②其次,當用戶點擊頁面中的某個按鈕提交表單時,瀏覽器會將這個_VIEWSTATE的隱藏域也一起提交到服務(wù)端;服務(wù)器端在解析請求時,會將瀏覽器提交過來的ViewState進行反序列化后填充到ViewState屬性中(比如下圖中,我們可以通過一個軟件將_VIEWSTATE解碼得到一個如下圖所示的樹形結(jié)構(gòu));再根據(jù)業(yè)務(wù)處理需要,從這個屬性中根據(jù)索引找到具體的Value值并對其進行操作;操作完成后,再將ViewState進行Base64編碼再次返回給瀏覽器端;
③因此,我們可以得出一個結(jié)論:VIEWSTATE適用于同一個頁面在不關(guān)閉的情況下多次與服務(wù)器交互(PostBack)。這里我們也可以通過下圖來溫習(xí)一下ViewState的流程,ViewState存放著“事故現(xiàn)場”,下次可以方便地“還原現(xiàn)場”,將無狀態(tài)的Http模擬成了有狀態(tài)的,也讓廣大的初學(xué)者了解不到無狀態(tài)的這個特性。
1.3 喜歡就會放肆—又愛又恨的ViewState!
事實上,除了我們手動在服務(wù)器端向ViewState屬性中添加的K/V對數(shù)據(jù),我們在aspx.cs代碼中為某些服務(wù)器控件設(shè)置的值(例如:為Repeater設(shè)置DataSource中存入的數(shù)據(jù)集、為Label所設(shè)置的Text內(nèi)容等,但不包括:TextBox、CheckBox、CheckboxList、RadioButtonList)都存入了ViewState中。這樣做的話,我們下次再向服務(wù)器提交請求時,現(xiàn)有表單中所有的服務(wù)器控件狀態(tài)都會記錄在ViewState中提交到服務(wù)器,在服務(wù)器端可以方便地對這些服務(wù)器控件進行有狀態(tài)的操作并返回,這無疑是讓我們歡喜的,因為方便了我們的開發(fā)過程,提高了我們的開發(fā)效率;
但有人說:“喜歡就會放肆”,ViewState讓人又愛又恨啊。例如,在我們使用Repeater的過程中,WebForm會自動將DataSource(數(shù)據(jù)源,你可以理解為一個集合)存儲到ViewState中并返回給瀏覽器。可以參考下面的例子來實地理解一下:
①含有Repeater的aspx頁面:
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 產(chǎn)品名稱 10 </td> 11 <td> 12 產(chǎn)品描述 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> View Code②后臺代碼模擬從數(shù)據(jù)庫中取得數(shù)據(jù)集合并綁定到Repeater中:
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 = "統(tǒng)一方便面", 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 } View Code編譯生成后,通過查看此頁面的html代碼,可以明顯看到一長串的_VIEWSTATE隱藏域。將此_VIEWSTATE復(fù)制到ViewStateDecoder中進行反編碼,可以發(fā)現(xiàn)它確實存儲了Repeater中的數(shù)據(jù)集合。這里我們不禁要問:展示數(shù)據(jù)既然已經(jīng)渲染成了html,為何還要存儲在ViewState隱藏域中?如果我們的數(shù)據(jù)集合是一百行、一千行數(shù)據(jù)的話,那ViewState隱藏域豈不很大(100k?200k?)?但不幸的是,這是ViewState的設(shè)計機制,要想依靠它來保持狀態(tài),它就會將服務(wù)器控件的狀態(tài)包括數(shù)據(jù)集合都存儲到其中,在瀏覽器和服務(wù)器之間來回傳遞保持狀態(tài)。
這里就涉及到網(wǎng)站的性能問題的探討了:由于ViewState存儲在頁本身,因此如果存儲較大的值,用戶請求顯示頁面的速度會減慢(這對于互聯(lián)網(wǎng)系統(tǒng)來說,就是一個噩夢。你會選擇一個1秒內(nèi)響應(yīng)的網(wǎng)站瀏覽還是5秒內(nèi)響應(yīng)的網(wǎng)站?)。又因為ViewState會隨同F(xiàn)orm表單一同回傳給服務(wù)器,如果ViewState很大的話,Http報文也會很大,網(wǎng)站流量消耗也會增大。
那么,有沒有一種方法可以讓ViewState克制一下呢?別急,請看下面的介紹。
1.4 但愛就是克制—禁用還是不禁用ViewState?
剛剛說到,因為ViewState會一定程度上影響性能,所以我們可以在不需要的時候禁用 ViewState。默認情況下 ViewState 將被啟用,并且是由每個控件(而非頁面開發(fā)人員)來決定存儲在 ViewState 中的內(nèi)容。有時,這一信息對應(yīng)用程序并沒有什么用處(例如上面提到的Repeater的數(shù)據(jù)集合,已經(jīng)渲染生成了html顯示,還存儲了一份副本在ViewState里邊)。盡管也沒什么害處,但卻會明顯增加發(fā)送到瀏覽器的頁面的大小。因此如果不需要用ViewState,最好還是將它關(guān)閉,特別是當 ViewState 很大的時候。當然,ViewState幫我們實現(xiàn)了某些服務(wù)器控件狀態(tài)保持,因此在非必需的情況下,還是可以適度使用的,特別是在開發(fā)企業(yè)內(nèi)部信息系統(tǒng)的場景。
那么,怎樣來禁用ViewState呢?禁用ViewState又有什么策略呢?下面我們一一來探討。
①頁面級禁用ViewState:在aspx的首部的Page指令集中添加EnableViewState="false",該頁面中所有控件的狀態(tài)都不會存入ViewState的,頁面一下就會清爽許多;
<%@ Page Language="C#" AutoEventWireup="true" CodeBehind="RepeaterViewState.aspx.cs" Inherits="WebFormDemo.RepeaterViewState" EnableViewState="false" %>禁用后,再次查看生成的html代碼,我們會發(fā)現(xiàn):咦,_VIEWSTATE還在那兒,但是明顯比先前的體積小了不少!
再將這個瘦身后的_VIEWSTATE復(fù)制到ViewStateDecoder中進行反編碼查看,我們會發(fā)現(xiàn),只保存了一個最基本的信息,Repeater的那些數(shù)據(jù)集合沒有存入進去了。
PS:為什么禁用ViewState之后,頁面源代碼中仍然有_VIEWSTATE的隱藏域?
這是因為就算禁用了viewstate,aspx頁面中還是會有一個服務(wù)器控件在那里使用,這就是<form runat="server">。這時,如果你將form去掉runat="server",將其變?yōu)槠胀╤tml標簽,那么頁面就干凈了,從此_VIEWSTATE這個隱藏域徹底消失在你的頁面中。 ?
②控件級禁用ViewState:在某些場景中,我們只希望禁用某個控件(例如Repater)的ViewState,其他控件仍然通過ViewState保持狀態(tài)。這時,我們可以給指定的控件設(shè)置一個屬性EnableViewState="false"即可;
<asp:Repeater ID="repeaterProducts" runat="server" EnableViewState="false"> </asp:Repeater>③全局級禁用ViewState:園子里的大神老趙(Jeffrey Zhao)曾經(jīng)說過,“我如果新建一個WebForm項目,做的第一件事情就是去Web.config中將enableViewState設(shè)置為false從而將ViewState全局關(guān)閉”。那么,我們?nèi)绻M麑⒕W(wǎng)站中所有頁面的ViewState都禁用,總不可能去一個一個頁面得修改Page指令吧?ASP.Net為我們提供了一個配置,我們只需要在Web.config的system.web中增加一句配置即可:
<pages enableViewState="false" />PS:開發(fā)中也可以采用大神老趙的做法,先禁用,再選擇性啟用,畢竟沒有非要ViewState才能干成的事兒!
④真正的禁用ViewState:剛剛我們的三種方法實踐后,在頁面還是出現(xiàn)_VIEWSTATE的隱藏域,盡管它保留了最基本的信息。那么,我們可能會問?怎樣才能徹底地真正地禁用ViewState,根本就別給我生成_VIEWSTATE的隱藏域。答案是有的,將<form runat="server"/>的runat="server"去掉,就不會出現(xiàn)了,但那樣又會偏離WebForm的開發(fā)模式,大部分的服務(wù)器控件都無法正常使用,開發(fā)效率又會有所損失。
綜上所述,在實際開發(fā)中應(yīng)該權(quán)衡利弊,特殊情況特殊分析(到底這個場景該不該禁用ViewState),選擇是否禁用ViewState,采用何種方式禁用ViewState。對于ViewState的探秘本篇就到此為止,由于我本人理解的也不是很深刻,所以希望各位園友如果有理解,可以回復(fù)出來大家探討共同進步。
二、飛來的利器—UpdatePanel探秘
2.1 從一個簡單四則運算計算器說起
假如有以下一個場景,我們要做一個簡單的四則計算器。aspx頁面代碼和后端邏輯代碼如下:
(1)aspx頁面代碼
<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> View Code(2)后置邏輯代碼
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("除數(shù)不能為0!");}result = number1 / number2;break;}this.lblResult.Text = result.ToString();} } View Code生成后運行該頁面,可以達到以下的效果。我們輸入兩個數(shù)字后,選擇是加法、減法、還是乘除法后,點擊=按鈕,即可刷新頁面顯示運算結(jié)果。
在WebForm中,每一次點擊runat="server"的按鈕都會將調(diào)用form.submit將請求提交到服務(wù)器,服務(wù)器會返回新的頁面html進行頁面重繪。這是一個整頁的刷新操作,不符合AJAX的風格需求。因此,我們想要將其改為AJAX版本的,除了使用基本的XMLHttpRequest外,我們還可以使用基于JQuery的AJAX方案,這些都是輕量級的原生態(tài)的AJAX技術(shù)方案。但我們偉大的微軟(我哭啊,真是為我們考慮啊,連AJAX方案都為我們解決了,而且還提供了AJAX控件供我們使用,我們拖控件的習(xí)慣可以用到AJAX方案上了!!!)還為我們提供了一套叫做ASP.Net AJAX的技術(shù)方案,通過這套方案,我們可以在ASP.Net很容易地實現(xiàn)AJAX效果,甚至都不需要我們懂JavaScript。因此,也就出現(xiàn)了前些年,很多WebForm開發(fā)者陸續(xù)使用ASP.Net AJAX Extension進行AJAX開發(fā),紛紛表示:AJAX如此簡單,我等豈能不會?但是,雖然它簡單易行,由于其性能問題一直被人詬病,而我們這些菜鳥也未能了解其性能問題的原因,本著知其然也知其所以然的目標,現(xiàn)在我們來使用它并剖析它一下。
2.2 天上掉下個林妹妹—使用UpdatePanel控件
不得不說,UpdatePanel真的是天上掉下的林妹妹,一個神奇的控件!有了它,我們可以將頁面中需要進行局部刷新的內(nèi)容放到其ContentTemplate中,一個需要整頁刷新的操作便可以成為局部刷新。現(xiàn)在,我們首先來使用其改造剛剛的簡單四則計算器頁面。
(1)加入UpdatePanel,并將計算器html內(nèi)容拖入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)運行該頁面,通過開發(fā)人員工具查看Http請求
通過查看請求報文,我們了解到此次的請求響應(yīng)不再是返回整頁的html內(nèi)容,而只是我們放在了UpdatePanel里面的html內(nèi)容,頁面也沒有再刷新,于是不禁感嘆一句:AJAX,So easy!媽媽再也不用擔心我的頁面了!
2.3?直到看見XmlHttpRequest才是唯一的答案—UpdatePanel原來如此
正當我們沉浸在UpdatePanel為我們提供的神奇的AJAX世界里時,我們不禁對UpdatePanel為我們做了哪些工作產(chǎn)生了興趣。
(1)首先,我們知道AJAX的核心對象是XmlHttpRequest,那么原生態(tài)的AJAX請求的JS方法是如何寫的呢?
function ajax(url, onsuccess) {var xmlhttp = window.XMLHttpRequest ? new XMLHttpRequest() : new ActiveXObject('Microsoft.XMLHTTP'); //創(chuàng)建XMLHTTP對象,考慮兼容性。XHR xmlhttp.open("POST", url, true); //“準備”向服務(wù)器的xx.ashx發(fā)出Post請求(GET可能會有緩存問題)。這里還沒有發(fā)出請求 //AJAX是異步的,并不是等到服務(wù)器端返回才繼續(xù)執(zhí)行 xmlhttp.onreadystatechange = function () { if (xmlhttp.readyState == 4) //readyState == 4 表示服務(wù)器返回完成數(shù)據(jù)了。之前可能會經(jīng)歷2(請求已發(fā)送,正在處理中)、3(響應(yīng)中已有部分數(shù)據(jù)可用了,但是服務(wù)器還沒有完成響應(yīng)的生成) { if (xmlhttp.status == 200) //如果Http狀態(tài)碼為200則是成功 { onsuccess(xmlhttp.responseText); } else { alert("AJAX服務(wù)器返回錯誤!"); } } } //不要以為if (xmlhttp.readyState == 4) {在send之前執(zhí)行!!!! xmlhttp.send(); //這時才開始發(fā)送請求。并不等于服務(wù)器端返回。請求發(fā)出去了,我不等!去監(jiān)聽onreadystatechange吧! }(2)其次,通過查看運行頁面的html,我們可以發(fā)現(xiàn)加入UpdatePanel后,我們的html中多了這么幾個js引用。
(3)既然我們知道要發(fā)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; }由以上的方法名我們可以猜到,此方法是一個執(zhí)行AJAX請求的方法。在此方法中,創(chuàng)建了XmlHttpRequest對象,也使用了open方法指明以GET還是POST方法向服務(wù)器哪個處理程序發(fā)送請求,并且也為該請求指定了請求成功后需要執(zhí)行的回調(diào)函數(shù)方法(onreadystatechange),最后調(diào)用send方法正式發(fā)送請求
由此,我們可以初步分析出一個結(jié)論:UpdatePanel本質(zhì)還是幫我們封裝了以XmlHttpRequest為核心的一系列方法幫我們將CodeBehind中的同步事件變?yōu)榱水惒讲僮?#xff0c;并通過DOM更新指定的HTML內(nèi)容,使得我們可以方便地實現(xiàn)AJAX效果。
但是,我們也不由發(fā)出感嘆:本來可以很簡單地使用XmlHttpRequest來實現(xiàn)的東西,為什么使用UpdatePanel會引入這么多js,并且為我們返回的東西還是那么多(比如上面的例子,我只需要的數(shù)據(jù)是一個結(jié)果,卻給我返回一部分無用的html,還有一系列的hiddenId之類的數(shù)據(jù))。在對性能要求較高的應(yīng)用場合,如果使用UpdatePanel來實現(xiàn)AJAX會增加服務(wù)器的負載,并且會消耗掉不必要的網(wǎng)絡(luò)流量(比如每次請求都會來回都會發(fā)送ViewState里的數(shù)據(jù),在性能和數(shù)據(jù)量上都會造成損失)。園子里的浪子曾經(jīng)在他的博文《遠離UpdatePanel帶給我的噩夢》里邊寫到:“UpdatePanel在頁面小的時候還是很好用的,而當頁面控件數(shù)不斷上升的時候,UpdatePanel就開始直線下降,我們現(xiàn)在頁面有4,5百個控件,每做一次PostBack需要長達15秒鐘之長,實在讓人無法忍受。”
那么,有木有方式可以替換UpdatePanel呢?其實答案很簡單,那就是使用基于XmlHttpRequest的js方法,再加上一定的js回調(diào)函數(shù)即可。這就要求我們掌握javascript,不能只做拖UpdatePanel控件的程序員。現(xiàn)在基于js的JQuery庫也早已為我們封裝了XmlHttpRequest,提供了ajax開發(fā)的一系列方法供我們調(diào)用,相當于UpdatePanel的“重量級”來說,可謂是輕了不少,是一個“輕量級”的AJAX開發(fā)方式。通過借助jQuery Ajax+ashx可以方便地在.Net中進行Ajax開發(fā),并且具有不錯的性能,這也是我實習(xí)所在的企業(yè)中經(jīng)常用到的方式。
三、學(xué)習(xí)總結(jié)
本篇主要學(xué)習(xí)了WebForm中的狀態(tài)保持法寶—ViewState,以及曾經(jīng)的ASP.Net AJAX方案的利器—UpdatePanel,雖然一直在說這個不好,那個別用。但是,微軟之所以為我們提供了這些東西,肯定有它存在的理由,并不一定都是不好的東西。所謂利器在手,沒有一點內(nèi)功心法的人還是使用不好它,無法發(fā)揮出其100%的優(yōu)勢。因此,身為.Net學(xué)習(xí)者的我們,不能滿足于微軟為我們所提供的便利,要知其然也知其所以然,做一個上進的程序員,加油吧!
校園招聘的大潮就快來臨,希望園子里跟我一樣即將畢業(yè)的菜鳥們能夠好好復(fù)習(xí)基礎(chǔ),在招聘中贏得一份好offer,實現(xiàn)自己的價值!
轉(zhuǎn)載于:https://www.cnblogs.com/xbzhu/p/6707192.html
總結(jié)
以上是生活随笔為你收集整理的ASP.Net WebForm温故知新学习笔记:二、ViewState与UpdatePanel探秘的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: FTP的连接方式(防火墙的配置)
- 下一篇: P2617 Dynamic Rankin