做web前段也有一段时间了,对于web中js文件的加载有些体会想跟大家一起分享一下。
1.首先说说js文件的合并和压缩吧
为了便于集中式管理js的合并和压缩我们创建一个Js.ashx文件来专门处理合并压缩,这里我们借用Yahoo.Yui.Compressor工具来压缩我们的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; } } }
当我们在浏览器访问js时如:
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; ListjsKeys = jsFiles.AllKeys.OrderBy(x => x).ToList (); string jsFormat = " "; 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()); } }
这样在开发的时候我们书写代码就很方便了如:
这样把所有的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