这几年,网站做的越来越吸引人,而大部分吸引人的因素来自于脚本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更容易理解。我创建了一个例子(如果需要源码,请发送请求至我的邮箱:[email protected])。
下面是项目截图:

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
>
总结: 你可以在网上搜到很多类似的方法,我仅仅是把那些方法组合了一下。如果需要源码,请发邮件至我的邮箱:[email protected].