ASP.NET MVC Performance: Bundling And Minification

Introduction

This article introduces how to improve ASP.NET MVC Application performance with the help of bundling and minification. Both bundling and minification are the two separate techniques to reduce the load time. The bundling reduces the number of requests to the Server, while the minification reduces the size of the requested assets.

Most browsers process six requests simultaneously to each Website, which is why additional requests will be queued by the Browsers. If we reduce these requests, the queued time for the other requests will be reduced as well. Thus, bundling is used to reduce these requests from the Browser to Server.

The minification is the removal of all unnecessary characters from a text-based resource (JS and CSS) in a way that doesn’t alter the expected functionality. This means shortening identifiers, aliasing functions and removing the comments, white-space characters and new lines.

Both bundling and minification can be applied together but both have a separate process. As the minified and bundling version is hard to read and step through, we should not bundle and minify all CSS and JS files at a time of production or debug, while we must do bundling and minification of all the files, when we go live.

The ASP.NETMVC offers bundling and minification technique by System.Web.Optimization class, which exists under the System.Web.Optimization dll.

Bundling

The bundle is a logical group of physical files, which loads in a single HTTP request. We have separate CSS files, which can be loaded in a single request with the help of bundling. The bundling also can create for JavaScript files separately. A bundle can’t contain both CSS and JavaScript files. We need to create a separate bundle for CSS and JavaScript files. We create a bundle, based on the use of CSS or JS files in the Application. For example, an Application uses both the bootstrap and site CSS for UI design, due to which we create a common bundle for them, such as a core bundle. The following figure shows a logical grouping of a file to create a bundle.

Logical grouping of files-Bundle
Figure 1: Logical grouping of files-Bundle

We have an MVC Application and run it. After that we see HTTP requests which creates for individual files.

HTTP Requests
Figure2: HTTP Requests

The figure, shown above, shows that both bootstrap.css and site.css have individual HTTP requests, whose total load time is 281 ms. Now, we introduce the bundling for both CSS files. We create style bundle for those files. The BundleConfig class’s RegisterBundles is used to create both Script and Style bundle. The code snippet, given below, shows style bundle for both bootstrap and site CSS files.

  1. using System.Web;  
  2. using System.Web.Optimization;  
  3.   
  4. namespace BundlingSample  
  5. {  
  6.     public class BundleConfig  
  7.     {  
  8.         public static void RegisterBundles(BundleCollection bundles) {  
  9.   
  10.             bundles.Add(new StyleBundle("~/Content/css").Include(  
  11.                 "~/Content/bootstrap.css",  
  12.                 "~/Content/site.css",  
  13.                 "~/Content/bootstrap-responsive.css"));  
  14.         }  
  15.     }  
  16. }  
We add a style bundle in a bundle module. This style bundle contains the path of the files. The bundle name is a virtual path of the files. It is highly recommended that the bundle virtual path must not be same as the files virtual paths. This is a way to create a bundle in the Application.

Afterwards, we need to register this bundle in the Application. We call this method in the Application_Start method of the global.asax.cs file and register this bundle module to bundle config. We don’t need to register individual bundles in the Application. We need to just register bundle module in the Application for all style and script bundles.The code snippet, given below, shows global.asax.cs file.
  1. using System.Web.Mvc;  
  2. using System.Web.Optimization;  
  3. using System.Web.Routing;  
  4.   
  5. namespace BundlingSample   
  6. {  
  7.     public class MvcApplication: System.Web.HttpApplication  
  8.     {  
  9.         protected void Application_Start() {  
  10.             AreaRegistration.RegisterAllAreas();  
  11.             FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);  
  12.             RouteConfig.RegisterRoutes(RouteTable.Routes);  
  13.             BundleConfig.RegisterBundles(BundleTable.Bundles);  
  14.         }  
  15.     }  
  16. }  
Now, call this style bundle on the view, as per the code snippet, given below:
  1. @Styles.Render("~/Content/css")  
The bundle doesn’t work in the debug mode. Thus, we set the debug value false in web.config file, as shown in the snippet, given below:
  1. <system.web>  
  2.     <compilation debug="false" targetFramework="4.5.1" />  
  3.     <httpRuntime targetFramework="4.5.1" />  
  4. </system.web>  
Now, run the Application again. The style link has been changed in the following ways, as per bundling. The link represents that both bootstrap.css and site.css are combined in the single file.
  1. <link href="/Content/css?v=Bz3KZjU_pdOm2wAVr7z_ylCuQzQDs1O8N6pV4cvXc_Q1" rel="stylesheet"/>  
Now, we check the performance for these files. As both the files take a load time 281ms without bundling, while these takea  load time 141ms with bundling, as shown in the image, given below:

Load time for bundle
Figure 3: Load time for bundle

In the same way, we can also create a bundle for the script, which is called Script bundle. For example, we create a bundle for bootstrap JS file as per the code snippet, given below, which defined in RegisterBundles method of BundleConfig class. We can add multiple files path in a bundle, which will be separated by a comma.
  1. bundles.Add(new ScriptBundle("~/bundles/bootstrap").Include(  
  2. "~/Scripts/bootstrap.js"));  
We can also define the versioning in the bundle. The code, given below, shows that it always loads the latest version of jQuery file.
  1. bundles.Add(new ScriptBundle("~/bundles/jquery").Include(  
  2. "~/Scripts/jquery-{version}.js"));  
We can also combine the files that are in the same folder and have the same prefix or suffix with its name. Suppose we want to add all the script files, that exist within “~/Scripts” folder and have “jquery.validate” as a prefix, then we can create bundle as per code snippet, given below:
  1. bundles.Add(new ScriptBundle("~/bundles/jqueryval").Include( "~/Scripts/jquery.validate*"));  
Now, call this script bundle on the view as per the code snippet, given below:
  1. @Scripts.Render("~/bundles/jquery")  
Now, run the Application and see that all the JS files are converted to a single JS file, as shown below:
  1. <script src="/bundles/jquery?v=FVs3ACwOLIVInrAl5sdzR2jrCDmVOWFbZMY6g6Q0ulE1"></script>  
Now, we can compare the result with bundling and without bundling result. The figure shows the result with the bundling files.

Bundling result
Figure 4: Bundling result

To improve the performance, we can also load the resources from CDN. The code, given below, replaces the local jQuery bundle with a CDN jQuery bundle.
  1. bundles.UseCdn = true;  
  2. bundles.Add(new ScriptBundle("~/bundles/jquery","https://cdnjs.cloudflare.com/ajax/libs/jquery/3.1.0/jquery.js"));  
The CDN stands for Content Delivery Networks are the Servers, where the latest jQuery libraries are hosted like Google, Cloud Flare etc. The CDN helps to access or download the files, parallel to the files downloaded from our own Website and will be cached the first time. Sometimes, these CDN hosted files are not good for our Websites, because there are some issues like performances due to slow CDN Servers, even non-availability of the Servers etc.

The CDN resource request, shown above, fails in the debug mode as the bundling works in the release mode only. Thus, we retrieve jQuery resource locally, as per the code snippet, given below:
  1. @Scripts.Render("~/bundles/jquery") <  
  2.     script type = "text/javascript" >  
  3.     if (typeof jQuery == 'undefined')  
  4.     {  
  5.         var script = document.createElement('script');  
  6.         script.src = '@Url.Content("~/Scripts/jquery-1.10.2.js")';  
  7.         script.type = 'text/javascript';  
  8.         document.getElementsByTagName("head")[0].appendChild(script);  
  9.     } <  
  10.     /script>  
Minification

The Minification is a technique for removing unnecessary characters (white space, newline, tab), comments and short variable names from the text based files such as JavaScript and CSS files without expecting alter functionality to reduce the size, which causes improved load times of a Webpage. There are some tools available to minify JS and CSS files. We can download Visual Studio extension from link for minifying JS and CSS files.

Busting Browser's Cache by Bundling

As we upload the changes in the static resources such as CSS and JS files on the live server, the resources changes but it does not update on the Browser, because the Browser's cache resources are based on URLs automatically. Thus, when a Web page requests a resource, it checks in cache first. If the resource is found in cache, use cached copy rather than retrieving the resources from the Server. Hence, whenever you change the content of CSS and JS, files will not reflect on the Browser. For this, you need to force the Browser for refreshing/reloading.

The bundles set the HTTP expires header, one year from when the bundle is created. As we have a CSS bundle resource, which loads on the Browser with the following link.
  1. <link href="/Content/css?v=Bz3KZjU_pdOm2wAVr7z_ylCuQzQDs1O8N6pV4cvXc_Q1" rel="stylesheet"/>  
The /Content/css style bundle contains the query string pair v=Bz3KZjU_pdOm2wAVr7z_ylCuQzQDs1O8N6pV4cvXc_Q1. The query string v has a value token. This token is a unique identifier, which is used for caching. As long as the bundle /Content/css dosen’t change, the request for this bundle uses its same token. If any file in the bundle changes, ASP.NET optimization framework will generate a new token, guaranteeing the browser requests for the bundle will get the latest bundle.

We create bundling to improve an Application's performance, while it works in the release mode only but we develop an Application in the debug mode on our machine. Thus, we create a unique token for the resource path. We add a version key in the web.config, which will be used as a unique token in the development.
  1. <appSettings>  
  2.     <add key="Version" value="sa291988" />  
  3. </appSettings>  
Afterwards, create a class, where we define the format for both JavaScript and styles. The code snippet is given below for the same.
  1. using System.Configuration;  
  2.   
  3. namespace BundlingSample   
  4. {  
  5.     public class SiteKeys {  
  6.         public static string StyleVersion {  
  7.             get {  
  8.                 return "<link href=\"{0}?v=" + ConfigurationManager.AppSettings["version"] + "\" rel=\"stylesheet\"/>";  
  9.             }  
  10.         }  
  11.         public static string ScriptVersion {  
  12.             get {  
  13.                 return "<script src=\"{0}?v=" + ConfigurationManager.AppSettings["version"] + "\"></script>";  
  14.             }  
  15.         }  
  16.     }  
  17. }  
Now, render JS and CSS files on the views in the following ways.
  1. @Styles.RenderFormat(SiteKeys.StyleVersion,"~/Content/css")  
  2. @Scripts.RenderFormat(SiteKeys.ScriptVersion,"~/bundles/jquery")  
These techniques help to improve the Application's performance.

Other resources to improve ASP.NET MVC Application performance will be: