How to use bundling and minification in ASP .NET
Table of contents
What you will learn?
ASP .NET framework provides a native and powerful scripts optimization engine that is very easy to use. In this blog post, I will go through how you can handle your website scripts in an efficient way using bundle and minification techniques. After going through this step-by-step guide - you know how to optimize website scripts properly.
Let's jump in!
Installing the Web.Optimization package
First, make sure the project includes Microsoft.AspNet.Web.Optimization package.
If the package is missing - just install the library using NuGet command:
Install-Package Microsoft.AspNet.Web.Optimization
Preparing BundleConfig inside App_Start folder
Let's create BundleConfig.cs file under App_Start folder (if the folder doesn't exist just create it manually).
Then create the RegisterBundles method with the BundleCollection parameter inside the BundleConfig class. It is a good place to organize your scripts and group them into bundles.
public class BundleConfig
{
public static void RegisterBundles(BundleCollection bundles)
{
//create bundles and add them to BundleCollection
}
}
Creating bundles
You can create any number of bundles depending on your needs/modules (for instance home page bundles can be different than blog page bundles). It is worth experimenting and checking in practice - what combination of scripts/bundles will be most effective.
In the example below, we see two separate bundles - one for javascript files (ScriptBundle) and the second for CSS (StyleBundle). Each bundle should include relevant scripts.
Once bundles are ready, they should be added to BundleCollection as follows:
public class BundleConfig
{
public static void RegisterBundles(BundleCollection bundles)
{
ScriptBundle scriptBundle = new ScriptBundle("~/bundles/master/js");
scriptBundle
.Include("~/assets/js/jquery.js")
.Include("~/assets/js/plugins.js")
.Include("~/assets/js/functions.js");
bundles.Add(scriptBundle);
StyleBundle styleBundle = new StyleBundle("~/bundles/master/css");
styleBundle
.Include("~/assets/css/plugins.css")
.Include("~/assets/css/style.css")
.Include("~/assets/css/custom.css");
bundles.Add(styleBundle);
BundleTable.EnableOptimizations = true;
}
}
Forcing optimization
In the next step, you should disable debug mode and force optimization:
<system.web>
<compilation debug="false" />
</system.web>
BundleTable.EnableOptimizations = true
Bundling and modification won't work without these settings.
Registering the bundling engine
Bundles should be registered when starting the application, so go to Global.asax.cs file and find Application_Start method. If it doesn't exist, just create it end execute 'BundleConfig.RegisterBundle(BundleTable.Bundles)' inside as follows:
protected void Application_Start(Object sender, EventArgs e)
{
BundleConfig.RegisterBundle(BundleTable.Bundles);
}
Bundling and minification in Razor Views:
How to use bundles in Razor Views?
First of all, make sure ~/View/Web.config file contains the new namespace
<add namespace="System.Web.Optimization"/>
Then, include the namespace in your Razor View, so you could use the @Scripts and @Styles helpers:
@using System.Web.Optimization;
Finally, you can render the bundles in the View using helpers:
@Scripts.Render("~/bundles/master/js")
@Styles.Render("~/bundles/master/css")
Your master page can look like this:
<!DOCTYPE html>
<html lang="en-US">
<head>
@Styles.Render("~/bundles/master/css")
</head>
<body>
@RenderBody()
@Scripts.Render("~/bundles/master/js")
</body>
</html>
Bonus tip: Resolving browser cache issues
It often happens that after making some scripts changes and new code release, we have unexpected feedback from the QA team/Client:
"It's not working" or "I can't see the changes".
Sounds familiar? 😀
Most of the time it's caused by the browser's caching mechanism.
How to avoid caching issues?
There is a good old trick to make sure the browser will work with the appropriate versions of scripts/bundles.
It’s really easy to force the browser cache to use the latest CSS/JS bundles by appending the query string parameter at the end of each bundle.
<link href="/bundles/master/css" rel="stylesheet"/>
Each bundle should have a unique URL after each build.
<link href="/bundles/master/css?v=rJwD8GQBqCoQ6sc6KrlHuWYflkjwRDAm2RK3fPv7aOk1" rel="stylesheet"/>
After googling here and there I found a great code snippet here on StackOverflow.
All credit goes to author Ranjeet patil (thanks)!
internal static class BundleExtensions
{
public static Bundle WithLastModifiedToken(this Bundle sb)
{
sb.Transforms.Add(new LastModifiedBundleTransform());
return sb;
}
public class LastModifiedBundleTransform : IBundleTransform
{
public void Process(BundleContext context, BundleResponse response)
{
foreach (var file in response.Files)
{
var lastWrite = File.GetLastWriteTime(HostingEnvironment.MapPath(file.IncludedVirtualPath)).Ticks.ToString();
file.IncludedVirtualPath = string.Concat(file.IncludedVirtualPath, "?v=", lastWrite);
}
}
}
}
Using this clean and awesome CSharp extension method - we get an elegant and effective code inside BundleConfig.cs:
public class BundleConfig
{
public static void RegisterBundles(BundleCollection bundles)
{
ScriptBundle scriptBundle = new ScriptBundle("~/bundles/master/js");
scriptBundle
.Include("~/assets/js/jquery.js")
.Include("~/assets/js/plugins.js")
.Include("~/assets/js/functions.js");
bundles.Add(scriptBundle.WithLastModifiedToken());
StyleBundle styleBundle = new StyleBundle("~/bundles/master/css");
styleBundle
.Include("~/assets/css/plugins.css")
.Include("~/assets/css/style.css")
.Include("~/assets/css/custom.css");
bundles.Add(styleBundle.WithLastModifiedToken());
BundleTable.EnableOptimizations = true;
}
}
Now, after each build - you will get a unique extra parameter in your script URL:
<link href="/bundles/master/css?v=rJwD8GQBqCoQ6sc6KrlHuWYflkjwRDAm2RK3fPv7aOk1" rel="stylesheet"/>
This trick should resolve browser caching problems and make visitors happy.
Bundles output
After going through all the steps above, you should get nice and warm bundles output:
CSS bundle output:
<link href="/bundles/master/css?v=rJwD8GQBqCoQ6sc6KrlHuWYflkjwRDAm2RK3fPv7aOk1" rel="stylesheet"/>
Js bundle output:
<script src="/bundles/master/js?v=8kmHc-ukmg3rp-jj2rNMjYYIG_lP3ErMNtowPG93XbM1"></script>
In the end, good luck to you and happy bundling & minification 😀