How to handle bundling and minification in ASP .NET

What you will learn?

In this blog post, I will go through how you can handle your website scripts in an efficient way using bundle and minification techniques.

ASP .NET framework provides a native and powerful way of script optimization and I often use it in my solutions.

After going through this step-by-step guide - you should be able to apply this solution to your website.

Let's go!

#1 Installing the package

First, make sure your project includes Microsoft.AspNet.Web.Optimization package.

If the package is missing - install the library using NuGet command:

Install-Package Microsoft.AspNet.Web.Optimization

#2 Prepare BundleConfig inside App_Start folder

For this, let's create BundleConfig.cs file to under App_Start folder if the folder doesn't exist just create it manually.

Then, you can organize your scripts and split them into bundles.

Different combinations of bundles/scripts should be tested to get the best results.

In the example below, we create two separate bundles - one for javascript files and the other for CSS.

You can create any number of bundles depending on your needs.

For example, you can create different bundles for different modules - lightweight bundles can speed up your site 💨.

It is worth experimenting and checking in practice - what combination of scripts/bundles will be most effective.

At the end you should add the bundles to BundleCollection inside BundleConfig class:

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;
        }
    }

4# Force optimization

In the next step, you should disable debug mode and force optimization as follows:

<system.web>
     <compilation debug="false" />
</system.web>
BundleTable.EnableOptimizations = true

5# What about cache in the browser?

What to do when script changes are not visible to the end-user?

It often happens that after making some scripts changes and new code release, we have strange 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.

After a quick website source code review, we see:

<link href="/bundles/master/css" rel="stylesheet"/>

How to avoid those issues?

There is a good old trick to make sure the browser will not hold website scripts/bundles.

It’s really easy to force the browser cache to refresh CSS/js bundles by appending the query string parameter at the end of each bundle.

Each bundle should have a unique URL after each build.

After googling here and there I found 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;
        }
    }

This way - after each build - you will get unique extra parameter in your script URL:

<link href="/bundles/master/css?v=rJwD8GQBqCoQ6sc6KrlHuWYflkjwRDAm2RK3fPv7aOk1" rel="stylesheet"/>

6# Register the bundling engine

Now it's the right time to register the bundles in the Application_Start event inside Global.asax.cs file as follows: 

protected void Application_Start(Object sender, EventArgs e)  
{  
    BundleConfig.RegisterBundle(BundleTable.Bundles);  
}

7# Using 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>

Final result

As a result, 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>

That's it - Happy bundling & minification 😀

Comments
Leave a Comment