Click here to Skip to main content
14,694,012 members
Articles » Web Development » Caching » General
Article
Posted 6 Jun 2015

Tagged as

Stats

11.8K views
1 bookmarked

Cache busting for RequireJS / MVC projects

Rate me:
Please Sign up or sign in to vote.
5.00/5 (1 vote)
6 Jun 2015CPOL
Shows how to improve your ASP.NET MVC web site's performance through far future client side caching of RequireJS modules, while still forcing the browser to refresh its cache the moment you introduce a new version of your modules.

Contents

Introduction

RequireJS generates script tags to load modules. To increase web site performance, you want to configure IIS to send response headers that tell the browser to cache those modules for up to a year - the maximum according to the HTTP standard (why; how).

However, when you update one or more modules, you don't want your users to keep using the old versions for up to a year. You want them to update their caches right away.

In this article we'll see how to do cache busting based on the version of the assembly running your web site code. The download contains a working example.

Cache Busting 101

It is very easy to get a browser to refresh its cache. Take this script tag:

<script src="/Scripts/main.js" type="text/javascript"></script>

All you need to do is add some sort of version in a query string:

<script src="/Scripts/main.js?v=1.0.0.0" type="text/javascript"></script>

Then when you update the version:

<script src="/Scripts/main.js?v=1.1.0.0" type="text/javascript"></script>

The browser will look for main.js?v=1.1.0.0 in its cache, won't find it, and request it from the server.

Cache busting this way has pros and cons:

  • Advantage - Very simple. Because you're using a query string, there is no need to change the name of main.js on the server.
  • Disadvantage - Loss of caching performance. When you introduce the query string, proxies and the IIS kernel cache no longer cache your file.

The optimal cache busting method involves changing the file name itself, such as main.1.1.0.0.js instead of main.js?v=1.1.0.0. There are several packages that will do this for you on the fly (example), but they don't integrate with RequireJS. So we'll stick with query strings in the rest of this article.

Cache Busting with RequireJS

RequireJS lets you add a query string to all script tags it generates with the urlTags configuration option:

<span style="color: red;"><script type="text/javascript"> var require = { urlArgs: "v=1.0.0.0" }; </script></span>
<script data-main="Scripts/main" 
        src="http://cdnjs.cloudflare.com/ajax/libs/require.js/2.0.2/require.min.js" 
        type="text/javascript"></script>

If you run the sample site in the download in Chrome and inspect the DOM (right click anywhere on the page | Inspect element), you'll find that RequireJS has generated this:

Image 1

Using the assembly version

So far so good. However, you don't want to change your HTML manually each time you do a new release - too clumsy and error prone.

It's much more attractive to use the assembly version of your site:

  • Easy to update manually - right click your web site project | Properties | Assembly Information.
  • Can be updated automatically by TFS Build (how, how, how).

You can get the assembly version via the currently executing assembly:

string version = 
    System.Reflection.Assembly.GetExecutingAssembly().GetName().Version.ToString();

However, this won't work if your site is hosted on a shared hosting company that uses Medium Trust, such as GoDaddy, because the call to GetExecutingAssembly is not allowed for security reasons in such an environment.

Putting it together with an Html Helper

Lets first create an Html Helper that returns the assembly version:

namespace RequireJSAndVersioning.Helpers
{
    public static class AssemblyVersionHelper
    {
        public static string AssemblyVersion(this HtmlHelper helper)
        {
            return System.Reflection.Assembly.GetExecutingAssembly()
                         .GetName().Version.ToString();
        }
    }
}

Now import your helper's namespace in your razor file:

@using RequireJSAndVersioning.Helpers

Finally, use the helper with the RequireJS configuration section:

<script type="text/javascript">
    var require = {
        urlArgs: "v=<span style="color: red;">@Html.AssemblyVersion()</span>"
    };
</script>
<script data-main="Scripts/main" 
        src="http://cdnjs.cloudflare.com/ajax/libs/require.js/2.0.2/require.min.js" 
        type="text/javascript"></script>

As a result of all this, whenever the assembly version of your site changes, all your visitors' browsers will request the latest versions of your modules from your server.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

Share

About the Author

Matt Perdeck
Architect
Australia Australia
Twitter: @MattPerdeck
LinkedIn: au.linkedin.com/in/mattperdeck
Current project: JSNLog JavaScript Logging Package

Matt has over 9 years .NET and SQL Server development experience. Before getting into .Net, he worked on a number of systems, ranging from the largest ATM network in The Netherlands to embedded software in advanced Wide Area Networks and the largest ticketing web site in Australia. He has lived and worked in Australia, The Netherlands, Slovakia and Thailand.

He is the author of the book ASP.NET Performance Secrets (www.amazon.com/ASP-NET-Site-Performance-Secrets-Perdeck/dp/1849690685) in which he shows in clear and practical terms how to quickly find the biggest bottlenecks holding back the performance of your web site, and how to then remove those bottlenecks. The book deals with all environments affecting a web site - the web server, the database server and the browser.

Matt currently lives in Sydney, Australia. He recently worked at Readify and the global professional services company PwC. He now works at SP Health, a global provider of weight loss web sites such at CSIRO's TotalWellBeingDiet.com and BiggestLoserClub.com.

Comments and Discussions

 
-- There are no messages in this forum --