web優化之-mvc js動態合併 動態壓縮 去掉js重複引用 js快取 js延遲載入
做web前段也有一段時間了,對於web中js檔案的載入有些體會想跟大家一起分享一下。
1.首先說說js檔案的合併和壓縮吧
為了便於集中式管理js的合併和壓縮我們建立一個Js.ashx檔案來專門處理合並壓縮,這裡我們借用Yahoo.Yui.Compressor工具來壓縮我們的js檔案
程式碼如下:
當我們在瀏覽器訪問js時如:public class Js : IHttpHandler { public void ProcessRequest(HttpContext context) { context.Response.ContentType = "text/javascript"; HttpRequest request = context.Request; HttpResponse response = context.Response; if (!request.QueryString.AllKeys.Contains("href")) { response.Write("No Content"); } else { string href = context.Request.QueryString["href"].Trim(); string[] files = href.Split(new string[]{",",","},StringSplitOptions.RemoveEmptyEntries); foreach (string fileName in files) { string filePath = context.Server.MapPath(fileName); if (File.Exists(filePath)) { string content = File.ReadAllText(filePath, Encoding.UTF8); content = JavaScriptCompressor.Compress(content); response.Write(content); } else { response.Write("\r\n未找到原始檔"+filePath+"\r\n"); } } } } public bool IsReusable { get { return false; } } }
http://localhost:58798/js.ashx?href=scripts/jquery.lazyload.js,scripts/jquery.validate.js
返回結果如圖:
但是在實際開發中很多專案為了追求js的合併和壓縮,開發很不友好把js的引用放在一個地方,寫了很長的一串啊,如上面js引用。
下面說說如何改善吧:
public static class Extensions { const string jsFileKey = "JSFileKey"; static string jshandlerUrl = string.Empty; public static string JsHandlerUrl { get { if (string.IsNullOrEmpty(jshandlerUrl)) { jshandlerUrl = ConfigurationManager.AppSettings["jsHandlerUrl"] ?? string.Empty; } return jshandlerUrl; } } public static void AppendJsFile(this HtmlHelper htmlHelper, string jsFile, int group = 1) { NameValueCollection jsFiles = null; if (htmlHelper.ViewContext.HttpContext.Items.Contains(jsFileKey)) { jsFiles = htmlHelper.ViewContext.HttpContext.Items[jsFileKey] as NameValueCollection; } else { jsFiles = new NameValueCollection(); htmlHelper.ViewContext.HttpContext.Items.Add(jsFileKey, jsFiles); } if (jsFiles.AllKeys.Contains(group.ToString())) { string fileUrl = jsFiles[group.ToString()]; if (!fileUrl.Contains(jsFile)) jsFiles.Add(group.ToString(), jsFile); } else { jsFiles.Add(group.ToString(), jsFile); } htmlHelper.ViewContext.HttpContext.Items[jsFileKey] = jsFiles; } public static MvcHtmlString RenderJsFile(this HtmlHelper htmlHelper) { NameValueCollection jsFiles = null; StringBuilder content = new StringBuilder(); if (htmlHelper.ViewContext.HttpContext.Items.Contains(jsFileKey)) { jsFiles = htmlHelper.ViewContext.HttpContext.Items[jsFileKey] as NameValueCollection; List<string> jsKeys = jsFiles.AllKeys.OrderBy(x => x).ToList<string>(); string jsFormat = "<script type=\"text/javascript\" src=\"{0}\"></script>"; foreach (string key in jsKeys) { string jsFile = jsFiles[key]; content.AppendFormat(jsFormat, JsHandlerUrl + jsFile); //htmlHelper.ViewContext.HttpContext.Response.Write(string.Format(jsFormat, JsHandlerUrl + jsFile)); } } return new MvcHtmlString(content.ToString()); } }
這樣在開發的時候我們書寫程式碼就很方便了如:
@{Html.AppendJsFile("Scripts/jquery.lazyload.js");}
這樣把所有的js檔案快取起來,最後在呼叫 @Html.RenderJsFile() 一次性全部輸出js。如:
至於為什麼這樣寫我就不多說了,這種思想在mvc中的RenderPartial和RenderAction都是可用的,如
重複的js引用也在Js.ashx去掉是不是很方便啊
最後生成的html程式碼:
2.下面我們來看看js的延遲載入,為了實現js延遲載入我們需要引用相關的js,在這裡我用的是lazyload.js,具體請參考http://blog.csdn.net/dz45693/article/details/7529584
延遲載入後的效果
在文件載入前只加載了一個2.2k的lazyload.js檔案,其他的js檔案都在ready後加載。
相應的html程式碼
3.下面再來看看js的快取吧,快取涉及到要做服務端和客戶端快取,客戶端快取說白了就是做304返回
修改後的程式碼:
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;
if (!request.QueryString.AllKeys.Contains("href"))
{
response.Write("No Content");
}
else
{
string href = context.Request.QueryString["href"].Trim();
string[] files = href.Split(new string[] { ",", "," }, StringSplitOptions.RemoveEmptyEntries);
CacheItem item = null;
object obj = HttpRuntime.Cache.Get(href);//服務端快取
if (null == obj)
{
StringBuilder allText = new StringBuilder();
foreach (string fileName in files)
{
string filePath = context.Server.MapPath(fileName);
if (File.Exists(filePath))
{
string content = File.ReadAllText(filePath, Encoding.UTF8);
content = JavaScriptCompressor.Compress(content);
//response.Write(content);
allText.Append(content);
}
else
{
// response.Write("\r\n未找到原始檔"+filePath+"\r\n");
allText.Append("\r\n未找到原始檔" + filePath + "\r\n");
}
}//end foreach
item = new CacheItem() { Content = allText.ToString(), Expires = DateTime.Now.AddHours(1) };
HttpRuntime.Cache.Insert(href, item, null, item.Expires, TimeSpan.Zero);
}
else
{
item = obj as CacheItem;
}
if (request.Headers["If-Modified-Since"] != null && TimeSpan.FromTicks(item.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(item.Content);
SetClientCaching(response, DateTime.Now);
}
}//end else href
}
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);
}
執行效果如圖:程式碼下載地址:http://download.csdn.net/detail/dz45693/4272920