加速ASP.NET页面--组合,最小化和压缩Javascript文件

这几年,网站做的越来越吸引人,而大部分吸引人的因素来自于脚本Javascript. 程序员们使用不同的js库和框架来使得网页更吸引人和人性化。

我们的问题是什么?

      1.你使用了太多的JS。而JS从不同的地方引用,有时候文件的总大小一度达到500K,甚至更多,这是影像网速一个致命的问题,特别是对于一些网络连接速度慢的人来说。解决这个问题的最好的方法是使用gzip来压缩JS文件(一会我们会详细讲解).

      2.JS文件在浏览器中的加载方法是one by one,也就是说,加载各种不同的JS文件需要请求多次,而且请求的时间是有你的网速有关的。如果在你的网页中有非常多的JS文件,在网页加载的同时就会造成一个非常大的延迟。大家可以使用Firebug(一个非常流行而且必须的Firefox组件)来测试一下。

      这不是CSS或者IMAGE的情况。如果你想引用多个CSS文件或者IMAGE的话,它们也会被一起加载。

      最好的解决方案是组合多个JS文件到一个文件中,这样就会去掉在读取多个JS文件时的延迟。

OK,说了半天,我们应该怎么做?

答案非常简单!组合多个文件并且压缩它们,将请求降至最低。接下来我们要通过一些步骤来实现这些功能。但是我们要怎么做呢?

最简单的方式使用HTTP Hanlder组合JS文件并且来压缩请求。这样就能代替多个JS文件的引用所导致的多个请求。首先,我们先像这样引用这个handler

<script type="text/javascript" src="ScriptCombiner.axd?s=Site_Scripts&v=1"></script>

 ScriptCombiner.axd是我们的HTTP Handler。有两个参数要传递给这个Handler,s和v. 

S代表设置的名称,V代表版本。你可以利用参数改变你的版本号,这样可以保证浏览器每次都加在一个新的JS文件,而不是从cache中取出。S设置名称表示被Handler处理的JS的文件列表。我们将这个列表保存在一个txt文档中,文件在App_Data文件夹中。文件包括我们需要引用的JS文件的相对路径。格式如下:

~/Scripts/ScriptFile1.js   
~/Scripts/ScriptFile2.js  

    大部分的工作在HTTP Handler中完成。下面我来解释一下在Handler中发生了什么:

1.从TXT文件中取得JS文件列表,并且加在这个JS文件列表。

2.去读每个JS文件中的内容,并且把这些内容组合成一个字符串。

3.使用Minifier来剔除字符串中的注释和空格回车。

4.使用Gzip来压缩字符串。

5.将压缩的结果放入Cache中,这样,我们就不必再次处理每次请求。

接下来,我们需要做一些步骤来使得这个handler更容易理解。我创建了一个例子(如果需要源码,请发送请求至我的邮箱:xiaocaige@vip.sina.com)。

下面是项目截图:

HTTP Handler 包括一些helper方法。我来简单介绍一下这些方法

CanGzip方法检测浏览器是否支持gzip.

代码
private bool CanGZip(HttpRequest request)   
{   
    
string acceptEncoding = request.Headers["Accept-Encoding"];   
    
if (!string.IsNullOrEmpty(acceptEncoding) &&   
         (acceptEncoding.Contains(
"gzip"|| acceptEncoding.Contains("deflate")))   
       
return true;   
    
return false;   
}  

大部分工作都有ProcessRequest来完成 

代码
 1 public void ProcessRequest(HttpContext context)   
 2 {   
 3     this.context = context;   
 4     HttpRequest request = context.Request;           
 5   
 6   
 7     string setName = request["s"?? string.Empty;   
 8     string version = request["v"?? string.Empty;   
 9   
10   
11     bool isCompressed = this.CanGZip(context.Request);   
12   
13   
14     if (!this.WriteFromCache(setName, version, isCompressed))   
15     {   
16         using (MemoryStream memoryStream = new MemoryStream(8092))   
17         {   
18                        using (Stream writer = isCompressed ? (Stream)(new ICSharpCode.SharpZipLib.GZip.GZipOutputStream(memoryStream)) : memoryStream)                   
19             {   
20                                 StringBuilder allScripts = new StringBuilder();   
21                 foreach (string fileName in GetScriptFileNames(setName))   
22                     allScripts.Append(File.ReadAllText(context.Server.MapPath(fileName)));   
23   
24                                 var minifier = new JavaScriptMinifier();   
25                 string minified = minifier.Minify(allScripts.ToString());   
26   
27                               byte[] bts = Encoding.UTF8.GetBytes(minified);   
28                 writer.Write(bts, 0, bts.Length);   
29             }   
30               byte[] responseBytes = memoryStream.ToArray();   
31             context.Cache.Insert(GetCacheKey(setName, version, isCompressed),   
32                 responseBytes, null, System.Web.Caching.Cache.NoAbsoluteExpiration,   
33                 CACHE_DURATION);   
34   
35                       this.WriteBytes(responseBytes, isCompressed);   
36         }   
37     }   
38 }  

 我们使用sharpZipLib来执行压缩。这是一个.NET的轻量级的组件。 

using (Stream writer = isCompressed ?  (Stream)(new GZipStream(memoryStream, CompressionMode.Compress)) : memoryStream)

最后我们来做的更好一些

我们可以将整个处理过程作的更方便使用。 

代码
 1 public static string GetScriptTags(string setName, int version)   
 2 {   
 3     string result = null;  
 4 #if (DEBUG)               
 5         foreach (string fileName in GetScriptFileNames(setName))   
 6         {   
 7             result += String.Format("\n<script type=\"text/javascript\" src=\"{0}?v={1}\"></script>", VirtualPathUtility.ToAbsolute(fileName), version);   
 8         }  
 9 #else   
10     result += String.Format("<script type=\"text/javascript\" src=\"ScriptCombiner.axd?s={0}&v={1}\"></script>", setName, version);  
11 #endif   
12     return result;   
13 }  

 GetScriptFileName用来描述之前返回的文件名的数组 

代码
 1 private static string[] GetScriptFileNames(string setName)   
 2 {   
 3     var scripts = new System.Collections.Generic.List<string>();   
 4     string setPath = HttpContext.Current.Server.MapPath(String.Format("~/App_Data/{0}.txt", setName));   
 5     using (var setDefinition = File.OpenText(setPath))   
 6     {   
 7         string fileName = null;   
 8         while (setDefinition.Peek() >= 0)   
 9         {   
10             fileName = setDefinition.ReadLine();   
11             if (!String.IsNullOrEmpty(fileName))   
12                 scripts.Add(fileName);   
13         }   
14     }   
15     return scripts.ToArray();   
16 }  

 我们需要在页面中引用如下:

<%= ScriptCombiner.GetScriptTags("Site_Scripts"1%>

最后,我们需要设置web.config.将Http Handler加入。 

代码
 1 <configuration>  
 2     <system.web>  
 3         <httpHandlers>  
 4             <add verb="POST,GET" path="ScriptCombiner.axd" type="ScriptCombiner, App_Code"/>  
 5         </httpHandlers>  
 6     </system.web>  
 7     <!-- IIS 7.0 only -->  
 8     <system.webServer>  
 9         <handlers>  
10             <add name="ScriptCombiner" verb="POST,GET" path="ScriptCombiner.axd" preCondition="integratedMode" type="ScriptCombiner, App_Code"/>  
11         </handlers>  
12     </system.webServer>  
13 </configuration>  

 

总结: 你可以在网上搜到很多类似的方法,我仅仅是把那些方法组合了一下。如果需要源码,请发邮件至我的邮箱:xiaocaige@vip.sina.com.

posted @ 2010-04-16 13:58  gybsoft  阅读(1107)  评论(3编辑  收藏  举报