Asp.net 动态压缩脚本或样式 (网页加载性能提升)

随着jquery、javascript插件越来越多,一些门户站引用的js文件也越来越多
我们随便打开一个网页,鼠标右键查看源代码会发现head区域了加载了N个js文件,导致网页的加载速度非常慢。

当浏览器遇到一个标签时,会运行里面的javascript代码,然后继续解析,依次往下执行,在此期间下一个脚本是等待下载或等待运行的状态,直到第一个脚本执行完毕,如果引用的是外部文件,浏览器首先会下载外部文件的代码,然后解析并运行代码。试想一下,如果head区域放了很多脚本文件,页面解析和用户交互是被完全阻塞的。

下面为大家提供几种优化方案:

⒈ 为了减小阻塞,于是部门网站采用了如下优化,将引用代码加到结束标签之前
1
2
3
4
5
6
7
8
9
<html>
<head></head>
<body>
aaaaaaaaaaaaaaaaaaaaaa
bbbbbbbbbbbbbbbbbbbbbb
<script type="text/javascript" src="a.js"></script>
<script type="text/javascript" src="b.js"></script>
</body>
</html>

原理:将所有引用脚本放在底部,尽量减少对整个页面下载的影响,这种优化方式不会让网页出现一片空白,但是脚本与脚本之间还是存在下载的阻塞!

⒉ 延迟脚本和异步脚本

script 中定义了6种属性,其中asyncdefer是我们优化要用到的。
async:表示立即下载脚本,但不妨碍页面中其他的操作,比如下载其他资源或等待加载其他脚本。只对外部脚本文件有效。
defer:表示脚本可以延迟到文档完全被解析和显示之后再执行
这2个属性都能使脚本在执行时不会影响页面的构造。但asyc并不保证按照指定它们的先后顺序执行的。

⒊ 多个脚本合并压缩加载(推荐)

上面解释了脚本加载的原理,很显然为了解决脚本与脚本之间的加载阻塞,可以减少脚本文件的引用数,一次请求加载完毕缓存到客户端,这样即减少了请求次数,也缓解了服务器的压力。这里我们需要用到Yahoo.Yui提供的压缩缩DLL :
Yahoo.Yui.Compressor.dll 和 Yahoo.Yui.Compressor.MsBuildTask.dll

咱们在项目里新建一个HttpCombiner.ashx 一般处理程序,专门用来处理请求的CSS和Javascript,服务端收到请求后,读取相应的文件合并压缩一起返回给客户端.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
public class HttpCombiner : IHttpHandler
{
class CacheItem
{
public string Content { set; get; }
public DateTime Expires { set; get; }
}
public void ProcessRequest(HttpContext context)
{
context.Response.ContentType = "text/javascript";
HttpRequest request = context.Request;
HttpResponse response = context.Response;
string[] allkeys = request.QueryString.AllKeys;
if (!allkeys.Contains("href") || !allkeys.Contains("type") || !allkeys.Contains("compress"))
{
response.Write("请求格式不正确,正确格式是type=....&href=....&compress=...");
response.Write("type只能是js或则css,compress只能是true或则false,href则是请求的文件,多个文件已逗号分隔");
}
else
{
string cacheKey = request.Url.Query;
#region /*确定合并文件类型*/
string fileType = request.QueryString["type"].Trim().ToLower();
string contenType = string.Empty;
if (fileType.Equals("js"))
{
contenType = "text/javascript";
}
else if (fileType.Equals("css"))
{
contenType = "text/css";
}
/*确定合并文件类型*/
#endregion
CacheItem cacheItem = HttpRuntime.Cache.Get(cacheKey) as CacheItem;//服务端缓存
if (cacheItem == null)
{
#region 合并压缩文件
/*合并文件*/
string href = context.Request.QueryString["href"].Trim();
string content = string.Empty;
string[] files = href.Split(new string[] { ",", "," }, StringSplitOptions.RemoveEmptyEntries);
StringBuilder sb = new StringBuilder();
foreach (string fileName in files)
{
string filePath = context.Server.MapPath(fileName);
if (File.Exists(filePath))
{
string readstr = File.ReadAllText(filePath, Encoding.UTF8);
sb.Append(readstr);
//content = JavaScriptCompressor.Compress(content);
//response.Write(content);
}
else
{
sb.AppendLine("\r\n未找到源文件" + filePath + "\r\n");
}
}
content = sb.ToString();
/*合并文件*/
/*压缩文件*/
string compressStr = request.QueryString["compress"].Trim();
bool iscompress = bool.Parse(compressStr);
if (iscompress)
{
if (fileType.Equals("js"))
{
content = JavaScriptCompressor.Compress(content);
}
else if (fileType.Equals("css"))
{
content = CssCompressor.Compress(content);
}
}
/*压缩文件*/
#endregion
cacheItem = new CacheItem() { Content = content, Expires = DateTime.Now.AddMinutes(30) };
HttpRuntime.Cache.Insert(cacheKey, cacheItem, null, cacheItem.Expires, TimeSpan.Zero);
}
response.ContentType = contenType;
if (request.Headers["If-Modified-Since"] != null && TimeSpan.FromTicks(cacheItem.Expires.Ticks - DateTime.Parse(request.Headers["If-Modified-Since"]).Ticks).Seconds < 100)
{
response.StatusCode = 304;
// response.Headers.Add("Content-Encoding", "gzip");
response.StatusDescription = "Not Modified";
}
else
{
response.Write(cacheItem.Content);
SetClientCaching(response, DateTime.Now);
}
} //合并文件结束
}
private void SetClientCaching(HttpResponse response, DateTime lastModified)
{
response.Cache.SetETag(lastModified.Ticks.ToString());
response.Cache.SetLastModified(lastModified);
//public 以指定响应能由客户端和共享(代理)缓存进行缓存。
response.Cache.SetCacheability(HttpCacheability.Public);
//是允许文档在被视为陈旧之前存在的最长绝对时间。
response.Cache.SetMaxAge(new TimeSpan(7, 0, 0, 0));
//将缓存过期从绝对时间设置为可调时间
response.Cache.SetSlidingExpiration(true);
}
public bool IsReusable
{
get
{
return false;
}
}
}

  • 前端调用就很简单明了了,用逗号分割开需要请求的文件
    1
    <script src="handles/HttpCombiner.ashx?type=js&compress=true&href=/Plugins/Highcharts/highcharts.js,/Plugins/Easytabs/easytabs.js" type="text/javascript"></script>

若是压缩css文件,将上面的type修改成css即可。compress 参数表示是否启用压缩。