AJAX 應用程序體系結構,第 1 部分

    目  錄
AJAX 派上用場
AJAX 體系結構
什麼是 AJAX 框架?
ASP.NET AJAX Extensions
部分呈現一覽
剖析 AJAX 回發
部分呈現的優化技術
評價部分呈現

        無論您是系統管理員、設計人員還是開發人員,您的工作都會受到 AJAX 的重大影響。管理員必須確保安全防護足夠高,以便應對各種可能的新類型攻擊。Intranet 管理員則必須保證任何瀏覽器上都沒有禁用 JavaScript。Web 設計人員需要面對一些新的挑戰,因爲 AJAX 可實現的功能是以前無法實現或不切實際的。而開發人員則需要熟悉新的 API 和新的全面編程方法。儘管如此,AJAX 對架構師來說究竟有何意義?
        AJAX 應用程序具有挑戰性,因爲它們引入了全新的概念和新的基礎。AJAX 模式橫跨客戶端和服務器環境,因此架構師角色必不可少。對於確定客戶端上發生的邏輯和處理與服務器上保留的內容,以及確定客戶端和服務器上的哪些數據對象能夠交換,清晰的體系結構設想顯得舉足輕重。
 
        AJAX 派上用場
        Web 最初用於共享信息的靜態頁面。短短几年之後,它發展成一種更加動態的媒介。如今,Web 由交互性極強的頁面所組成;然而,它仍非常依賴於以整頁轉換爲基礎的模式。用戶與頁面進行的交互越多,必須創建及返回用戶瀏覽器的頁面就越頻繁。以此方式使用交互式頁面所引起的負面結果有很多,例如,減緩頁面刷新和閃爍。這是一種不完善的解決方案,經常會導致糟糕的用戶體驗。AJAX 使得此模式中發生了非常令人渴望的變化,它允許更簡潔的 Web 應用程序,併爲生成新的應用程序奠定了基礎。AJAX 以最終用戶爲核心。使用 AJAX 功能,Web 應用程序的交互性變得更強,響應更及時,更快速且更友好。交互和響應(操作緩慢時的反饋)都更妥善地得到了處理,便於用戶對頁面進行更多操作。總之,用戶獲得了更好的體驗。
        使用 AJAX,更多代碼會在瀏覽器上執行,這要求將更多腳本指定給客戶端頁面。但是,這留下了一些大問題有待解決。何種腳本?誰將編寫此腳本?此腳本遵循哪些體系結構原則和模式?
        在本專欄中,我將從體系結構角度介紹 AJAX,幫助您、開發人員和架構師做出明智選擇。同時,我將爲設計人員和管理員提供足夠的上下文信息,讓他們的工作變得更加輕鬆。這是一個相當大的主題,因此我把它分爲兩部分來講。請不要錯過下個月的專欄,屆時我將繼續這一討論。


        AJAX 體系結構
        如果您要考慮 AJAX,請看圖 1,該圖說明了這一步的體系結構含義。傳統的 Web 應用程序需要在服務器上執行所有操作,應用程序只是偶爾會發出腳本代碼,在客戶端上運行任務,如處理數據驗證。

圖 1 傳統 Web 模式與 AJAX 模式 (單擊該圖像獲得較大視圖)
        AJAX 應用程序使用負責對 Web 服務器發出調用的客戶端框架。AJAX 服務器端框架則負責請求數據饋送並將其返回客戶端。這通常是 JavaScript Object Notation (JSON) 數據流,但也可使用其他格式,如 XML、RSS 和 CSV。
客戶端接收數據饋送,並使用 JavaScript 更新 UI。該服務器通過返回以指定格式編碼的原始數據,對請求做出響應。帶寬消耗會降至最低,應用程序速度會提高(因爲完成請求所用時間較少),而且 UI 更新也能在無可見回發的情況下生效。但是,在解決衆多問題的同時,客戶端上操作的增加也帶來了新的問題,如新的編碼實踐、新的安全隱患、可訪問性問題等。
 
        什麼是 AJAX 框架?
        爲了讓 AJAX 在網頁中有效,必須滿足一些條件。首先,您的瀏覽器必須能夠支持 JavaScript,此外還需要全時 Internet 連接,因爲 AJAX 應用程序無法脫機工作。當所有請求都在頁面級生成時,由於它們帶有非 AJAX 應用程序,瀏覽器可通過這些頁面的緩存集來提供導航,因此能夠脫機進行。而在與使用 AJAX 應用程序的瀏覽器合作的基於腳本的框架中,同樣的行爲則必須進行顯式編碼。這一領域有很多新東西涌現,並且一些新的幫助工具正在開發中。讓 AJAX 應用程序脫機工作,這對衆多的軟件供應商來說是一個挑戰。
        任何真正的 AJAX 應用程序都需要一個特定框架。通常,首選框架會在單獨的客戶端和服務器部分明確說明。
        AJAX 頁面中有兩種主要類型的腳本代碼:首選框架的系統級代碼和實現頁面預期行爲的用戶級代碼。系統級代碼提供用於向 Web 服務器發送異步請求並處理響應的引擎。用戶級代碼實質上使用文檔對象模型 (DOM) 腳本更新頁面的 UI。這裏便遇到我先前提及的其中一個問題。由誰編寫哪段?爲討論這點,我將重點介紹一種特別的 AJAX 框架 — ASP.NET AJAX Extensions。


        ASP.NET AJAX Extensions
        ASP.NET AJAX Extensions,即 ASP.NET 2.0 的一個擴展,爲新網站和現有網站提供 AJAX 功能。ASP.NET AJAX 有兩種編程模型:部分呈現和遠程服務。它們應用程序中採用兩個全然不同的體系結構,因而具有不同的優缺點。(您可能已經領會到,AJAX 中的多數功能都涉及權衡問題。)
        簡而言之,部分呈現允許您維護與傳統 ASP.NET 2.0 應用程序類似的體系結構。它只提供了一套新的服務器端工具,您可以用其來實現無閃爍頁面更新。
而遠程服務則涉及由腳本相對較多的 AJAX 前端所調用的面向服務的後端。幾乎所有的基本應用程序進程(包括身份驗證、數據分頁和分類)都必須重新設計。服務器端代碼必須分解爲特定於應用程序的服務,並且必須選擇一種格式(如 JSON)用於數據交換。最後,必須安排一個前端,特別注意將您的編碼限制爲 UI 級任務,並保持多數業務邏輯避開客戶端。
        遠程服務方法提供了更完整的 AJAX 體驗,而部分呈現對現有應用程序中已有的功能進行漸進式增強,從而提供了更流暢的轉換。
        這兩種方法在某種程度上可以結合使用。例如,您可以保留同一傳統 Web 體系結構,在各處添加一些無閃爍更新,然後花時間以面向服務的方式重構某些關鍵功能。ASP.NET AJAX Extensions 提供了幾個關鍵因素,但創建重要 AJAX 應用程序的主要工作還是取決於用戶。圖 2 顯示了 ASP.NET AJAX Extensions 框架的客戶端和服務器組件。 

文件 位置 說明
MicrosoftAjax.js 客戶端 包含使用面向對象的結構來擴展 JavaScript 的功能。它會擴展內置 JavaScript 對象的原型,並定義新的幫助器類,如 StringBuilder。
MicrosoftAjaxWebForms.js 客戶端 包含形成網絡堆棧的 JavaScript 類和實現客戶端部分呈現引擎的所有類。
MicrosoftAjaxTimer.js 客戶端 表示新的 AJAX System.Web.UI.Timer 服務器控件的客戶端對象模型。這基本上是瀏覽器計時器對象的服務器包裝。
System.Web.UI.ScriptManager System.Web.Extensions 協調下載適當的 JavaScript 文件和客戶端數據,包括 AJAX 庫、遠程服務的代理類、腳本文件的本地化版本以及全局化數據。
System.Web.UI.UpdatePanel System.Web.Extensions 定義可通過 AJAX 回發以無閃爍方式進行刷新的頁面區域。
System.Web.UI.UpdateProgress System.Web.Extensions 定義一個服務器端模板,當部分呈現操作花費時間太長時,可以向用戶提供反饋。
System.Web.UI.Timer System.Web.Extensions 子客戶端計時器超時時回發 AJAX 方式的幫助器服務器控件。

圖2 ASP.NET AJAX Extensions 框架的組件

        部分呈現一覽
        Jeff Prosise 在 2007 年 6 月的“超酷代碼”專欄 (msdn.microsoft.com/msdnmag/issues/07/06/WickedCode) 提供了 ASP.NET AJAX 部分呈現的精彩討論。從體系結構觀點看,部分呈現不添加任何新內容。它只是使用某些 AJAX 功能來增強舊應用程序的一種絕佳方法 — 其中最重要的是無閃爍頁面更新。
        部分呈現不需要學習許多新技能,它對現有代碼的影響也非常有限。對於可能會因採用 AJAX 而受影響的應用程序方面,例如安全性和可訪問性,部分呈現還提供了簡明的回調機制。一般來說,如果在升級過程中您無法將現有代碼的某些部分轉變成 AJAX,那麼可以讓這些代碼塊保持原樣,然後繼續。
        部分呈現請求通常稱爲 AJAX 回發。該術語十分恰當,可準確表示實際發生的情況。AJAX 回發與傳統 ASP.NET 回發相似,不同的是,前者由客戶端 ASP.NET雖然表面上類似純 AJAX 遠程調用,但 AJAX 回發看上去好像是對 ASP.NET 運行時組件的普通回發請求 JAX 庫中定義的一段腳本代碼來執行。雖然表面上類似純 AJAX 遠程調用,但 AJAX 回發看上去好像是對 ASP.NET 運行時組件的普通回發請求。到達服務器之後,該請求便會經歷典型回發請求的生命週期,並引發諸如 Init、Load 和 PreRender 等事件。在服務器上,AJAX 回發與傳統 ASP.NET 回發的區別僅在於用來呈現最終標記的算法不同而已。不同的算法是提高性能和沒有頁面閃爍的關鍵。但是,該應用程序模型仍與 ASP.NET 中的相同。圖 3 顯示了 AJAX 回發請求的已修改的生命週期。

圖 3 AJAX 回發的生命週期 
        除了呈現階段的實現之外,AJAX 回發和 ASP.NET 回發中的所有情況均相似。AJAX 回發仍會觸發通知事件(如 Load 和 PreRender),並且它仍會處理視圖狀態信息,並引發狀態更改和回發事件(如 TextChanged 和 Click)。
        ASP.NET AJAX 頁面必須包括 ScriptManager 控件的一個實例。此控件是 ASP.NET AJAX 頁面真正的控制中心。它負責將頁面鏈接到任何所需的框架腳本文件,並在檢測到發生 AJAX 回發時協調部分呈現。ScriptManager 控件會檢查請求中的 HTTP 頭,以確定該請求是否爲 AJAX 回發。以下是 MicrosoftAjaxWebForms.js 文件的摘錄,該文件可在觸發 AJAX 請求之前設置 HTTP 頭:
   request.get_headers()['X-MicrosoftAjax'] = 
       'Delta=true';
        要了解使得 AJAX 回發呈現階段不同的因素,請仔細查看以下 System.Web.Extensions 程序集的摘錄:
   protected override void OnPreRender(EventArgse){
       base.OnPreRender(e);
       if (IsInAsyncPostBack) 
           PageRequestManager.OnPreRender();
   }
        特別是,上述代碼段均來自 ScriptManager 控件。如您所見,腳本管理器會註冊一個預呈現事件處理程序。觸發時,處理程序會檢測它是否屬於 AJAX 回發,如果是,則從 PageRequestManager 內部類調用一個方法。PageRequestManager 類的 OnPreRender 方法會執行以下操作:
   internal void OnPreRender()
   {
       _owner.SetRenderMethodDelegate(
          newRenderMethod(this.RenderPageCallback));
   }
        該方法會設置特定的子例程來呈現當前請求的標記,非常簡單。SetRenderMethodDelegate 是在 System.Web.UI.Control 類中定義的方法。此方法不作公用,它基本上分配一個事件處理程序,頁面或控件使用該處理程序來呈現其內容。在先前的代碼段中,該方法會在當前頁面被調用,並以覆蓋當前請求的整個呈現過程而結束。因而,負責 AJAX 回發標記的真正代碼在 RenderPageCallback 方法(PageRequestManager 類中的內部方法)中定義。(有關 ScriptManager 控件的更多信息,請參閱本期《MSDN® 雜誌》中 Ben Rush 的文章。)
        基本上,AJAX 回發的已修改呈現過程會遍歷頁面上所有涉及回發並累積每一個標記的可更新面板。所有標記塊會與隱藏字段和任何錯誤信息一起打包到響應流,並傳遞給客戶端。現在讓我們深入剖析示例頁面上下文中的典型部分呈現調用。


剖析 AJAX 回發
若要使 ASP.NET 頁面成爲部分呈現的頁面,首先必須向頁面添加一個腳本管理器,然後通過使用 UpdatePanel 控件進行封裝,從而定義可獨立更新的區域。例如:
<asp:ScriptManager runat="server" />
<asp:UpdatePanel runat="server" ID="UpdatePanel1">
   <ContentTemplate>
      <%-- Markup of the region goes here --%>
   </ContentTemplate>
</asp:UpdatePanel>
        UpdatePanel 控件不會以任何方式改變爲該區域生成的可見標記,而只是向原始標記添加一個外圍 <div> 標記:
<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 免費下載。
<%@ Page Language="VB" CodeFile="Test.aspx.vb" Inherits="Test" %>

<html  >
<head runat="server">
    <title>Test :: Partial Rendering</title>
</head>
<body>
    <form id="form1" runat="server">
    <div>
        <asp:ScriptManager runat="server" ID="ScriptManager1" /> 
        
    <asp:UpdatePanel runat="server" ID="UpdatePanel1">
        <ContentTemplate>
            <asp:TextBox runat="server" ID="TextBox1" /> 
            <asp:Button runat="server" ID="Button1" Text="Update" /> 
            <hr />
            <asp:Label runat="server" ID="Label1" />
        </ContentTemplate>
    </asp:UpdatePanel>
    </div>
    </form>
</body>
</html>

圖4 示例頁面
        AJAX 回發的響應是一個文本流,可將其視爲具有大小、類型、ID 和內容列的記錄表格。每條記錄都稱爲一個 delta 節點。圖 5 顯示了您獲得的示例頁面表格。

大小 類型 ID 內容
249 updatePanel UpdatePanel1 <input name="TextBox1" type="text" value="Test" id="TextBox1" /> <input type="submit" name="Button1" value="Update" id="Button1" /> <hr /> <span id="Label1"></span>
52 hiddenField __VIEWSTATE  
56 hiddenField __EVENTVALIDATION  
0 asyncPostBackControlIDs    
0 postBackControlIDs    
13 updatePanelIDs   tUpdatePanel1
0 childUpdatePanelIDs    
12 panelsToRefreshIDs UpdatePanel1  
2 asyncPostBackTimeout 90  
9 formAction   Test.aspx
25 pageTitle   測試 :: 部分呈現
圖 5 示例頁面的 Delta 節點表格
        delta 節點標識了 AJAX 回發期間可能出現的更改。並非所有的響應都有相同的 delta 節點集,這取決於請求的服務器生命週期內發生的事件及原始標記的結構。
圖 6 列出了目前支持的所有 delta 節點。負責處理 delta 表格的 JavaScript 代碼位於 MicrosoftAjaxWebForms.js 文件中。
 
        部分呈現的優化技術
        查看圖 6 之後,您會發現響應由兩大塊組成:標記和視圖狀態。事件驗證數據(ASP.NET 2.0 的安全相關功能)、自定義隱藏字段、腳本和任何其他類型的節點通常總共只有幾十個字節。視圖狀態的大小是 ASP.NET 頁面的老問題了。遺憾的是,使用 ASP.NET AJAX 的部分呈現並未修正該問題。您仍需要精簡您的視圖狀態,從而減少下載時間。那麼如何減少頁面的標記呢?實際上,您可以使用更小的面板來恢復特定點擊所需的最少標記數量。 

Delta 節點類型 說明
updatePanel 存儲部分呈現區域的已更新標記。
hiddenField 存儲隱藏字段的內容。
arrayDeclaration 使用腳本管理器上適當的 RegisterXXX 方法來存儲添加到響應的數組聲明。
scriptBlock 使用腳本管理器上適當的 RegisterXXX 方法來存儲添加到響應的腳本塊。指定的腳本會根據您使用的 RegisterXXX 方法執行或插入頁面。
expando 使用腳本管理器上適當的 RegisterXXX 方法來存儲添加到響應的 expando 屬性。處理響應時,指定值會分配到指定的屬性 ID。
onSubmit 使用腳本管理器上適當的 RegisterXXX 方法來存儲添加到響應的 onsubmit 腳本塊。
asyncPostBackControlIDs 存儲調用過程中作爲更新面板異步觸發器註冊的各控件的 ID。
postBackControlIDs 存儲調用過程中作爲更新面板回發觸發器註冊的各控件的 ID。
updatePanelIDs 存儲涉及調用的更新面板的 ID。
asyncPostBackTimeout 以秒爲單位存儲請求的超時。
childUpdatePanelIDs 存儲調用過程中刷新的任何嵌套更新面板的 ID。
panelsToRefreshIDs 存儲調用期間刷新的任何更新面板的 ID,包括以編程方式刷新的面板。
formAction 存儲操作表單的 URL。
dataItem 存儲在服務器上生成並將由客戶端組件使用的任何附加信息。
dataItemJson 存儲在服務器上生成並將由客戶端組件使用的任何附加 JSON 序列化信息。
scriptDispose 使用腳本管理器上的 RegisterDispose 方法來存儲添加到響應的釋放腳本。指定的腳本在客戶端上針對指定的 DOM 元素來執行。
pageRedirect 存儲重定向時可用的新 URL。
error 存儲回發期間發生異常時的錯誤信息。
pageTitle 存儲頁面的新標題。
focus 存儲具有輸入焦點的新控件的 ID。
圖6 部分呈現 Delta 節點類型
        再次查看圖 4 中的代碼。頁面中的邏輯相當簡單,但一針見血。基本上,用戶單擊按鈕時,TextBox 的所有內容都會用來更新標籤。
        頁面只包含一個涵蓋以下所有內容的 UpdatePanel 控件:文本框、按鈕、分隔符和標籤。邏輯上來說,這是可以接受的。如果您需要指定讓頁面中某個區域自行刷新,這是一個可供選擇的方案。但是,在這種方式定義下,該區域包含了在回發中沒有更新的重要部分代碼。例如,TextBox 和 Button 控件不在此頁面中更新,且 <hr> 分隔符爲靜態純文本。
        更好的方式是減少 UpdatePanel,以便包含唯一的 Label 控件 — 由按鈕觸發的任意回發中更新的唯一服務器控件。通常,您需要仔細確定控件之間的回發關係(基本上檢查哪個更新哪個),並設計可更新面板,以包括用於指定用戶操作的最少控件數。在圖 4 所示的示例上下文中,有一個更佳方案:
<asp:TextBox runat="server" ID="TextBox1" /> 
<asp:Button runat="server" ID="Button1" Text="Update" 
     OnClick="Button1_Click" /> 
<hr />
<asp:UpdatePanel runat="server" ID="UpdatePanel1">
   <ContentTemplate>
       <asp:Label runat="server" ID="Label1" />
   </ContentTemplate>
   <Triggers>
      <asp:AsyncPostBackTrigger ControlID="Button1" />
   </Triggers>
</asp:UpdatePanel>
        現在 UpdatePanel 控件只包含 Label 控件。這意味着文本框和按鈕控件的(未更改)標記將不會返回。默認情況下,UpdatePanel 控件會在以下情況下刷新:它的任一個子控件引起回發或頁面上另一 UpdatePanel 進行了刷新。 此行爲可通過某些公共屬性來更改,即 UpdateMode、ChildrenAsTriggers 和 Triggers。
        特別是,UpdateMode 可決定 UpdatePanel 是設置爲無條件刷新(默認情況)還是在特定條件下刷新。如果您希望有條件刷新(優化可更新面板的關鍵設置),請先將 UpdateMode 設置爲 Conditional:
<asp:UpdatePanel runat="server" ID="UpdatePanel1" 
     UpdateMode="Conditional">
        但是,請注意,有條件更新僅在頁面上有多個面板時才適合。
某些情況下,您會發現很難將頁面拆分爲一組規範化的面板。在這些情況下,有一項技術可以幫助您防止子項觸發回發。ChildrenAsTriggers 屬性是一個布爾屬性(默認值爲 true),它決定面板的子控件是否充當 AJAX 回發事件的觸發器。在以下代碼段中,Button1 控件不會引起面板刷新:
<asp:UpdatePanel runat="server" ID="UpdatePanel1" 
     ChildrenAsTriggers="false">
   <ContentTemplate>
       <asp:Button runat="server" ID="Button1" Text="Update" 
          OnClick="Button1_Click" /> 
       <asp:Label runat="server" ID="Label1" />
   </ContentTemplate>
</asp:UpdatePanel>
        在如此配置的面板中單擊子按鈕時,AJAX 回發仍然會執行與該按鈕關聯的回發代碼,例如 Button1_Click 方法,但不會返回標記以刷新面板。
與有條件刷新相關的第三個屬性是 Triggers。這是一個集合屬性,列出了支配面板刷新的控件事件,如下所示:
<asp:UpdatePanel runat="server" ID="UpdatePanel1">
   <ContentTemplate>
       <asp:Label runat="server" ID="Label1" />
   </ContentTemplate>
   <Triggers>
      <asp:AsyncPostBackTrigger ControlID="Button1"
           EventName="Click" />
   </Triggers>
</asp:UpdatePanel>
        在本例中,Button1 的 Click 事件將刷新該面板。Button1 控件可以放在頁面的任何位置。請注意,我談論的是服務器事件。換言之,如果您要在用戶更改下拉列表的選擇時刷新面板,則必須先將 AutoPostBack 屬性添加到該下拉列表。
AJAX 回發期間服務器上發生狀態更改事件時,您仍可以配置一個面板進行刷新。請考慮以下代碼:
<asp:TextBox runat="server" ID="TextBox1" /> 
<asp:UpdatePanel runat="server" ID="UpdatePanel1">
   <ContentTemplate>
       <asp:Label runat="server" ID="Label1" />
   </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 方案要小。而且毫無疑問,用戶不會感覺到閃爍。

圖7 優化可更新面板的好處 (單擊該圖像獲得較大視圖)
        但是,需要提出警告的是,具有大量視圖狀態的頁面不會從部分呈現中獲益很多。您可以節省 5KB 的標記,但如果您需要 30KB 視圖狀態的情況,則無濟於事。
但在某些情況下,視圖狀態僅僅是由其他原因引起的問題的無辜替罪羊。假設 ASP.NET 頁面中有一個日曆控件,您使用可更新面板使刷新保持流暢:
<asp:UpdatePanel runat="server" ID="UpdatePanel1">
   <ContentTemplate>
       <asp:Calendar runat="server" ID="Calendar1" />
   </ContentTemplate>
</asp:UpdatePanel>
        此片段的標記不超過 7KB,如果您添加一些樣式,則可能增加到 10KB。現在,如果您禁用了該日曆的視圖狀態,可節省大約 1KB 的數據,而這僅僅是總大小的 10%。這是因爲,日曆就標記而言是一個較大的控件。視圖狀態對它的影響微乎其微。ASP.NET AJAX 頁面不應使用日曆控件來實現日期選擇功能。實際上,AJAX Control Toolkit 提供了一個擴展程序來代替,即 CalendarExtender 控件,當將其添加到普通文本框時,便可讓您選擇日期,有效地節省了 10KB 的標記。
每當文本框獲得輸入焦點時,此代碼都會彈出一個日曆:
<asp:textbox runat="server" ID="TextBox1" />
<act:CalendarExtender ID="CalendarExtender1" runat="server"
    TargetControlID="TextBox1"   
    OnClientDateSelectionChanged="updateLabel"
    Format="dd/MM/yyyy" />

<asp:UpdatePanel runat="server" ID="UpdatePanel1" 
     UpdateMode="Conditional">
    <ContentTemplate>
        <asp:label runat="server" ID="Label1" />
    </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 上開設的博客。

 

 

       歡迎關注:炬源信息技術網 (http://www.hugesoft.net/)

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章