js文件合并,压缩,缓存,延迟加载

来源:岁月联盟 编辑:exp 时间:2012-05-03

做web前段也有一段时间了,对于web中js文件的加载有些体会想跟大家一起分享一下。

1.首先说说js文件的合并和压缩吧

为了便于集中式管理js的合并和压缩我们创建一个Js.ashx文件来专门处理合并压缩

代码如下:

[csharp]
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引用。
 
下面说说如何改善吧:
[csharp]
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()); 
       } 
   } 
这样在开发的时候我们书写代码就很方便了如:
[csharp]
@{Html.AppendJsFile("Scripts/jquery.lazyload.js");} 
下面我们来看看js的延迟加载,为了实现js延迟加载我们需要引用相关的js,在这里我用的是lazyload.js,具体请参考http://www.2cto.com/kf/201205/130023.html

 延迟加载后的效果

在文档加载前只加载了一个2.2k的lazyload.js文件,其他的js文件都在ready后加载。
下面再来看看js的缓存吧,缓存涉及到要做服务端和客户端缓存
 
 

修改后的代码:

[csharp]
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://www.2cto.com/uploadfile/2012/0503/20120503021534861.zip
 



摘自 dz45693的专栏