優化網站設計(十四):使AJAX調用盡可能利用緩存特性


前言

網站設計的優化是一個很大的話題,有一些通用的原則,也有針對不同開發平台的一些建議。這方面的研究一直沒有停止過,我在不同的場合也分享過這樣的話題。

作為通用的原則,雅虎的工程師團隊曾經給出過35個最佳實踐。這個列表請參考  Best Practices for Speeding Up Your Web Site http://developer.yahoo.com/performance/rules.html,同時,他們還發布了一個相應的測試工具Yslow http://developer.yahoo.com/yslow/

我強烈推薦所有的網站開發人員都應該學習這些最佳實踐,並結合自己的實際項目情況進行應用。 接下來的一段時間,我將結合ASP.NET這個開發平台,針對這些原則,通過一個系列文章的形式,做些講解和演繹,以幫助大家更好地理解這些原則,並且更好地使用他們。

准備工作

為了跟隨我進行后續的學習,你需要准備如下的開發環境和工具

  1. Google Chrome 或者firefox ,並且安裝 Yslow這個擴展組件.請注意,這個組件是雅虎提供的,但目前沒有針對IE的版本。
    1. https://chrome.google.com/webstore/detail/yslow/ninejjcohidippngpapiilnmkgllmakh
    2. https://addons.mozilla.org/en-US/firefox/addon/yslow/
    3. 你應該對這些瀏覽器的開發人員工具有所了解,你可以通過按下F12鍵調出這個工具。
  2. Visaul Studio 2010 SP1 或更高版本,推薦使用Visual Studio 2012
    1. http://www.microsoft.com/visualstudio/eng/downloads
  3. 你需要對ASP.NET的開發基本流程和核心技術有相當的了解,本系列文章很難對基礎知識做普及。

本文要討論的話題

這一篇我和大家討論的是第十四條原則:Make Ajax Cacheable (使AJAX調用盡可能利用緩存特性)。

AJAX的基本概念

  1. AJAX = Asynchronous JavaScript and XML(異步的 JavaScript 和 XML)。
  2. AJAX 不是新的編程語言,而是一種使用現有標准的新方法。
  3. AJAX 是與服務器交換數據並更新部分網頁的藝術,在不重新加載整個頁面的情況下。

AJAX的典型應用場景

AJAX在目前的應用程序中使用非常廣泛,為網站提供了更加豐富的效果(雖然技術很早就有,但最早引起大家注意是在2004年左右的Gmail中)。其典型的應用場景包括

  1. 異步加載,使得頁面的內容可以分批加載。
  2. 局部更新,使得頁面的局部更新不會導致頁面的刷新。

由於AJAX其實也是需要發起請求,然后服務器執行,並將結果(通常是JSON格式的)發送給瀏覽器進行最后的呈現或者處理,所以對於網站設計優化的角度而言,我們同樣需要考慮對這些請求,是否可以盡可能地利用到緩存的功能來提高性能。

【備注】關於AJAX,以及它與目前的一些技術(主要是服務器端的技術)如何結合的文檔,我之前寫過很多,有興趣的朋友可以先參考一下 http://www.google.ee/search?q=site%3Awww.cnblogs.com%2Fchenxizhang%2F%20ajax

什么樣的AJAX請求可以被緩存?

對服務器請求進行優化的方法有很多,我之前已經寫過幾篇,這些原則也可以應用在AJAX的場景中

  1. 優化網站設計(三):對資源添加緩存控制
  2. 優化網站設計(四):對資源啟用壓縮
  3. 優化網站設計(九):減少DNS查找的次數
  4. 優化網站設計(十):最小化JAVASCRIPT和CSS
  5. 優化網站設計(十一):避免重定向
  6. 優化網站設計(十三):配置ETags

但是,對於AJAX而言,有一些特殊性,並不是所有的AJAX請求都是可以緩存的。這是由於AJAX的請求通常有兩種不同的方法:POST和GET。他們在進行請求的時候,就會略有不同。

  1. POST的請求,是不可以在客戶端緩存的,每次請求都需要發送給服務器進行處理,每次都會返回狀態碼200。(這里可以優化的是,服務器端對數據進行緩存,以便提高處理速度)
  2. GET的請求,是可以(而且默認)在客戶端進行緩存的,除非指定了不同的地址,否則同一個地址的AJAX請求,不會重復在服務器執行,而是返回304。

 

針對POST的情況如何優化

POST的請求,瀏覽器通常會假定用戶是想要提交(或者發送)數據給服務器,既然如此,那么瀏覽器自然就不會對該請求進行緩存,因為你是提交數據,所以它認為服務器自然每次都是需要處理的。我們可以來看一個例子。

using System;
using System.Web.Services;

namespace WebApplication4
{
    /// <summary>
    /// Summary description for HelloWebService
    /// </summary>
    [WebService(Namespace = "http://tempuri.org/")]
    [WebServiceBinding(ConformsTo = WsiProfiles.BasicProfile1_1)]
    [System.ComponentModel.ToolboxItem(false)]
    // To allow this Web Service to be called from script, using ASP.NET AJAX, uncomment the following line. 
 [System.Web.Script.Services.ScriptService]
    public class HelloWebService : System.Web.Services.WebService
    {

        [WebMethod]
        public string HelloWorld()
        {
            return string.Format("Hello,world -- {0}", DateTime.Now);
        }
    }
}

上面是一個簡單的XML Web Service的定義。需要注意的是,如果希望支持AJAX訪問的話,必須要添加ScriptService這個Attribute。

我們的調用代碼如下:

            //XML Web Service只支持POST,這種方式無法在瀏覽器中緩存,但可以結合服務器端的緩存,減少后台代碼執行的次數來提高性能
            $("#btCallXMLWebService").click(function () {
                $.ajax({
                    type: "POST",
                    contentType: "application/json;utf-8",
                    url: "HelloWebService.asmx/HelloWorld",
                    data: null,
                    dataType: "json",
                    success: function (result) {
                        alert(result.d);
                    }
                });
            });

運行起來之后,我們多次點擊這個按鈕,會截獲到如下的請求:

image

根據上面的截圖不難看出,其實每次都請求都是重新被處理過的,它們都是返回狀態碼為200。

這就是POST AJAX請求的處理情況,它不會被客戶端緩存。那你可能會說,能不能將type改為GET呢?例如下面這樣

          $("#btCallXMLWebService").click(function () {
                $.ajax({
                    type: "GET",
                    contentType: "application/json;utf-8",
                    url: "HelloWebService.asmx/HelloWorld",
                    data: null,
                    dataType: "json",
                    success: function (result) {
                        alert(result.d);
                    }
                });
            });

 

答案是,針對XML Web Service或者標准的WCF服務,它們不支持通過GET進行請求,只支持POST請求。

image

服務器返回了狀態碼為500的錯誤,並且在正文里面描述了這個錯誤的信息,如下圖所示

image

那么,針對這種場景,我們是否有什么方法進行優化呢?

using System;
using System.Web.Services;

namespace WebApplication4
{
    /// <summary>
    /// Summary description for HelloWebService
    /// </summary>
    [WebService(Namespace = "http://tempuri.org/")]
    [WebServiceBinding(ConformsTo = WsiProfiles.BasicProfile1_1)]
    [System.ComponentModel.ToolboxItem(false)]
    // To allow this Web Service to be called from script, using ASP.NET AJAX, uncomment the following line. 
    [System.Web.Script.Services.ScriptService]
    public class HelloWebService : System.Web.Services.WebService
    {

        /// <summary>
        /// 該方法被緩存了10秒鍾,是將結果緩存在服務器內存中。
        /// </summary>
        /// <returns></returns>
        [WebMethod(CacheDuration=10)]
        public string HelloWorld()
        {
            return string.Format("Hello,world -- {0}", DateTime.Now);
        }
    }
}

這樣修改之后,對於客戶端而言,其實沒有什么改變的,多次調用的時候,服務器都需要處理,然后返回狀態碼為200。但是區別是什么呢?區別在於服務器並不需要每次都運行真正的代碼,它將結果緩存在內存中,在10秒之內重復調用,就直接返回該內存中的數據即可。(這樣可以提高服務器的性能,提高並發性)

image

【備注】如果是WCF來做服務的話,默認是不支持直接對操作進行緩存的。

 

如何設計支持GET的服務

我們了解到默認情況下,XML Web Service和WCF服務,都只支持使用POST方法的AJAX調用。那么是否有辦法設計出來一個支持GET的服務呢?

  1. XML Web Service不支持GET
  2. WCF服務,可以通過一個特殊的webHttpBinding支持GET。本文將討論這一種做法。

【備注】WCF有多種適用場景,我之前寫過兩篇文章,有興趣的朋友可以參考

    WCF技術的不同應用場景及其實現分析   
    WCF技術的不同應用場景及其實現分析(續)

3. ASP.NET MVC中可以支持Web API這個功能,可以通過GET的方式進行調用。這個的做法,本文不做探討,有興趣的朋友可以參考 http://www.asp.net/web-api 

 

我們來看一個例子。在WCF中支持一種特殊的Operation,就是WebGet

using System;
using System.ServiceModel;
using System.ServiceModel.Activation;
using System.ServiceModel.Web;

namespace WebApplication4
{
    [ServiceContract(Namespace = "")]
   [AspNetCompatibilityRequirements(RequirementsMode = AspNetCompatibilityRequirementsMode.Allowed)]
    public class HelloWCFService
    {

     [WebGet(ResponseFormat = WebMessageFormat.Json)]
        public string RestfulHelloWorldWithParameter(string name)
        {
            return string.Format("Hello,{0} -- {1}", DateTime.Now, name);

        }
    }
}

 

AJAX調用代碼如下

            $("#btCallRestfulWCFServicebwithParameter").click(function () {

                var name = $("#txtName").val();

                //GET請求默認就是會被緩存(在同一個瀏覽器中,默認是臨時緩存,瀏覽器一關閉就刪除掉)
                $.getJSON("HelloWCFService.svc/RestfulHelloWorldWithParameter", { name: name }, function (data) {
                    alert(data.d);
                });
            });

運行起來之后,我們分別輸入不同的name參數,並且分別調用兩次。

image

我們可以發現,第一次調用會返回狀態碼(200),而第二次調用會返回狀態碼(304),但如果參數不一樣,又會返回狀態碼(200)。依次類推。

我們也確實可以在瀏覽器緩存中找到兩份緩存的數據

image

 

所以對於GET請求,默認就會被緩存。但是,如果你想改變這個行為,例如你有時候不想做緩存,應該如何來實現呢?

 

避免對GET請求做緩存

有的時候,我們可能希望GET請求不被緩存,有幾種做法來達到這樣的目的。

  1. 每次調用的時候,請求不同的地址(可以在原始地址后面添加一個隨機的號碼)例如下面這樣:
           $("#btCallRestfulWCFServicebwithParameter").click(function () {

                var name = $("#txtName").val();

                //GET請求默認就是會被緩存(在同一個瀏覽器中,默認是臨時緩存,瀏覽器一關閉就刪除掉)
                $.getJSON("HelloWCFService.svc/RestfulHelloWorldWithParameter", { name: name,version:Math.random() }, function (data) {
                    alert(data.d);
                });
            });

 

  1. 如果你所使用的是jquery的話,則可以考慮禁用AJAX的緩存
            $.ajaxSetup({ cache: false });

使用HTML5的新特性來減少不必要的AJAX調用

我覺得一個比較徹底的做法是,考慮將一部分數據緩存在客戶端中,而且最好不要在臨時文件夾中,以便下次啟動時還能使用到這些數據。HTML 5中提供了一個新的特性:local storage,可以很好地解決這個問題,如果有興趣的朋友可以參考下面的文檔

  1. http://w3school.com.cn/html5/html_5_webstorage.asp


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM