网站设计的优化是一个很大的话题,有一些通用的原则,也有针对不同开发平台的一些建议。这方面的研究一直没有停止过,我在不同的场合也分享过这样的话题。
作为通用的原则,雅虎的工程师团队曾经给出过35个最佳实践。这个列表请参考 Best Practices for Speeding Up Your Web Site http://developer.yahoo.com/performance/rules.html,同时,他们还发布了一个相应的测试工具Yslow http://developer.yahoo.com/yslow/
我强烈推荐所有的网站开发人员都应该学习这些最佳实践,并结合自己的实际项目情况进行应用。 接下来的一段时间,我将结合ASP.NET这个开发平台,针对这些原则,通过一个系列文章的形式,做些讲解和演绎,以帮助大家更好地理解这些原则,并且更好地使用他们。
为了跟随我进行后续的学习,你需要准备如下的开发环境和工具
这一篇我和大家讨论的是第十四条原则:Make Ajax Cacheable (使AJAX调用尽可能利用缓存特性)。
AJAX在目前的应用程序中使用非常广泛,为网站提供了更加丰富的效果(虽然技术很早就有,但最早引起大家注意是在2004年左右的Gmail中)。其典型的应用场景包括
由于AJAX其实也是需要发起请求,然后服务器执行,并将结果(通常是JSON格式的)发送给浏览器进行最后的呈现或者处理,所以对于网站设计优化的角度而言,我们同样需要考虑对这些请求,是否可以尽可能地利用到缓存的功能来提高性能。
【备注】关于AJAX,以及它与目前的一些技术(主要是服务器端的技术)如何结合的文档,我之前写过很多,有兴趣的朋友可以先参考一下 http://www.google.ee/search?q=site%3Awww.cnblogs.com%2Fchenxizhang%2F%20ajax
对服务器请求进行优化的方法有很多,我之前已经写过几篇,这些原则也可以应用在AJAX的场景中
但是,对于AJAX而言,有一些特殊性,并不是所有的AJAX请求都是可以缓存的。这是由于AJAX的请求通常有两种不同的方法:POST和GET。他们在进行请求的时候,就会略有不同。
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); } }); });
运行起来之后,我们多次点击这个按钮,会截获到如下的请求:
根据上面的截图不难看出,其实每次都请求都是重新被处理过的,它们都是返回状态码为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请求。
服务器返回了状态码为500的错误,并且在正文里面描述了这个错误的信息,如下图所示
那么,针对这种场景,我们是否有什么方法进行优化呢?
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秒之内重复调用,就直接返回该内存中的数据即可。(这样可以提高服务器的性能,提高并发性)
【备注】如果是WCF来做服务的话,默认是不支持直接对操作进行缓存的。
我们了解到默认情况下,XML Web Service和WCF服务,都只支持使用POST方法的AJAX调用。那么是否有办法设计出来一个支持GET的服务呢?
【备注】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参数,并且分别调用两次。
我们可以发现,第一次调用会返回状态码(200),而第二次调用会返回状态码(304),但如果参数不一样,又会返回状态码(200)。依次类推。
我们也确实可以在浏览器缓存中找到两份缓存的数据
所以对于GET请求,默认就会被缓存。但是,如果你想改变这个行为,例如你有时候不想做缓存,应该如何来实现呢?
有的时候,我们可能希望GET请求不被缓存,有几种做法来达到这样的目的。
$("#btCallRestfulWCFServicebwithParameter").click(function () { var name = $("#txtName").val(); //GET请求默认就是会被缓存(在同一个浏览器中,默认是临时缓存,浏览器一关闭就删除掉) $.getJSON("HelloWCFService.svc/RestfulHelloWorldWithParameter", { name: name,version:Math.random() }, function (data) { alert(data.d); }); });
$.ajaxSetup({ cache: false });
我觉得一个比较彻底的做法是,考虑将一部分数据缓存在客户端中,而且最好不要在临时文件夹中,以便下次启动时还能使用到这些数据。HTML 5中提供了一个新的特性:local storage,可以很好地解决这个问题,如果有兴趣的朋友可以参考下面的文档