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:
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.