Combine Multiple JavaScript and CSS Files and Remove Overheads

When developing a large web application especially if it is Ajax enabled, we often ended up with a larger number of JavaScript and Cascading Stylesheet files and it is quite common that more than one JavaScript file is involved for a single functionality. For example, if you are using DataTable widget of Yahoo User Interface you have to add yahoo-dom-event.js, connection-min.js, dragdrop-min.js, json.js, datasource-beta-min.js, datatable-beta-min.js . When a browser encounters an external resource file such as Image, CSS, JS it opens a separate connection to download the file which actually add extra overhead both to server and the browser. To minimize it we can manually combine multiple JS and CSS file into a single large file, but this makes development life harder. Instead we can programmatically combine these files which we will see in the next section.

To combine these resource file we can utilize the Generic Handler (.ashx)  file which comes with the Asp.net, we can also put some directive in the web.config file to conditionally combines the resource files depending upon the settings. First, lets see the web.config file.

<appSettings> <add key="UseFileSet" value="true"/> <add key="VersionNo" value="1.0"/> <add key="FileSet_CSS_Style1" value="Css/StyleSheet1.css,Css/StyleSheet2.css,Css/StyleSheet3.css,Css/StyleSheet4.css" /> <add key="FileSet_JS_Functionality1" value="Scripts/JScript1.js,Scripts/JScript2.js,Scripts/JScript3.js,Scripts/JScript4.js" /> </appSettings>

As you can see there three kind of settings, first whether we will use the file set which is specified in the consequent settings, second the version number which we will discuss later and third different file sets. Each fileset contains the fileset name as the key and files location in comma separated value. Now lets see how do we embed this resource files in an aspx page:

<% if (ResourceHandler.UseFileSet) { %>

<%@ WebHandler Language="C#" Class="ResourceHandler" %> using System; using System.IO; using System.Web; using System.Configuration;

public class ResourceHandler: IHttpHandler { public static bool UseFileSet { get { return Convert.ToBoolean(ConfigurationManager.AppSettings["UseFileSet"]); } }

public static string VersionNo { get { return ConfigurationManager.AppSettings["VersionNo"]; } }

public bool IsReusable { get { return true; } }

public void ProcessRequest (HttpContext context) { string fileSet = context.Request.QueryString["fileSet"];

//Basic Validation if (string.IsNullOrEmpty(fileSet)) { return; }

string contetType = context.Request.QueryString["type"];

if (string.IsNullOrEmpty(contetType)) { return; }

string files = ConfigurationManager.AppSettings["FileSet_" + fileSet];

if (string.IsNullOrEmpty(files)) { return; }

//Get the list of files specified in the FileSet string[] fileNames = files.Split(',');

if ((fileNames!= null) && (fileNames.Length > 0)) { //Write each files in the response for (int i = 0; i < fileNames.Length; i++) { context.Response.Write(File.ReadAllText(context.Server.MapPath(fileNames[i]))); }

//Set the content type context.Response.ContentType = contetType;

// Cache the resource for 7 Days CacheUtility.Cache(TimeSpan.FromDays(7)); } } }

As we can see the Handler contains some static property which we used when embedding in the aspx files.  The file merging is done in the ProcessRequest method. In this method we are reading the physical files which is specified in the web.config fileset and writing it in the Asp.net Response object and at the end we are using a helper class CacheUtility to cache the response. Now let see how the this class is implemented:

using System; using System.Web; using System.Reflection;

public static class CacheUtility { public static void Cache(TimeSpan duration) { HttpCachePolicy cache = HttpContext.Current.Response.Cache;

FieldInfo maxAgeField = cache.GetType.GetField("_maxAge", BindingFlags.Instance | BindingFlags.NonPublic); maxAgeField.SetValue(cache, duration);

cache.SetCacheability(HttpCacheability.Public); cache.SetExpires(DateTime.Now.Add(duration)); cache.SetMaxAge(duration); cache.AppendCacheExtension("must-revalidate, proxy-revalidate"); } }

As you can see we are not only using the regular caching methods of Asp.net, we are also using some reflection code to set the proper cache-control header. You can find the reason why the reflection is used from these two links Client-side caching for script methods access in ASP.NET AJAX and ASP.NET AJAX under the hood secrets. If you want to know more about the cache-control header then read Caching Details. If any of the files get modified then update the version number in web.config, it will ensure that the latest files get downloaded due to the query string.

Download: Complete Solution

kick it on DotNetKicks.com

This article is part of the GWB Archives. Original Author: Kazi Manzur Rashid

New on Geeks with Blogs

  • We Won The One Award I Actually Care About

    Full Scale made the Inc. 5000 for the fifth year straight, the 12th listing across my three companies. Here is why the one award you cannot buy is worth stopping for.

  • Your Customers Build the Features Now

    I let a tool I liked sit dead for a year rather than build the features I wanted. An MCP server meant I never had to, and your customers can do the same to your product.

  • Get the Size of a Directory in Linux the Easy Way

    du -sh for the quick answer, ncdu for the cleanup, df for the disk itself: every command for checking directory size in Linux, plus why du and df never agree.

  • Vim Search and Replace: The Ultimate Guide

    One :%s command replaces every match in a file before a find dialog would even open. The Vim substitute patterns worth the muscle memory: flags, ranges, capture groups, and multi-file edits.