加速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
ScriptCombiner.axd是我们的HTTP Handler。有两个参数要传递给这个Handler,s和v.
S代表设置的名称,V代表版本。你可以利用参数改变你的版本号,这样可以保证浏览器每次都加在一个新的JS文件,而不是从cache中取出。S设置名称表示被Handler处理的JS的文件列表。我们将这个列表保存在一个txt文档中,文件在App_Data文件夹中。文件包括我们需要引用的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.
{
string acceptEncoding = request.Headers["Accept-Encoding"];
if (!string.IsNullOrEmpty(acceptEncoding) &&
(acceptEncoding.Contains("gzip") || acceptEncoding.Contains("deflate")))
return true;
return false;
}
大部分工作都有ProcessRequest来完成
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的轻量级的组件。
最后我们来做的更好一些
我们可以将整个处理过程作的更方便使用。
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用来描述之前返回的文件名的数组
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 }
我们需要在页面中引用如下:
最后,我们需要设置web.config.将Http Handler加入。
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.