Browser side caching is awesome, it makes your pages load faster, reduces network usage and improves perceived load times but it starts to become a real pain when you build an application that frequently rolls out client-side updates. These updates propagate slowly to your end users with almost no way of being sure whether your bug still exists because you missed a test case or because the end user is simply still using the cached version of your JavaScript.

Let’s face it, you do tell your clients to press ctrl + f5 to see the latest changes you’ve made to JavaScript.

There are a few ways you can get around this problem, some of them being

  • Append a hash to the file.
  • Append a query string to the file

We will be looking at the former.

Gulp Rev To The Rescue!

The gulp plugin gulp-rev appends content hashes to the end of file names. This is great because hashes are only computed for files that have changed. This way, the client always only downloads the assets that have changed.

You need install gulp-rev by running the command

npm install gulp-rev –save-dev

Here we have installed gulp-rev as a development dependency. It is assumed that you already have gulp installed and a basic gulpfile running. Now, we create a task that handles the job of creating file revisions based on content hashes.

const gulp = require('gulp');
const rev = require('gulp-rev');
gulp.task("revision", function () {
    return gulp.src(["./Scripts/dist/**/*.js", "./Scripts/dist/**/*.css"])
        .pipe(rev())
        .pipe(gulp.dest("./Scripts/dist"))
        .pipe(rev.manifest())
        .pipe(gulp.dest("./Scripts"));
});

In the above snippet we select all JavaScript and CSS files and pipe it to the rev command and store the output in the folder named dist.

The rev.manifest() function creates a JSON mapping our original file names to the newly created file names with hashes. Below is what a manifest looks like.

{
  "assets/css/site.compiled.css": "assets/css/site-1db21d418d.compiled.css",
  "js/site.min.js": "js/site-ff3eec37bc.min.js",
  "vendors/vendors.js": "vendors/vendors-ebd24a3d51.js"
}

Serving Assets (ASP.NET)

Once the files are revised we can reference them in our HTML or JS files. The problem here is that every time the hash changes you would have to manually update each reference. Quite cumbersome, so let’s make it easy.

The below code samples pertain to ASP.NET and can be used similarly in the language of your choice.

In the below sample we create a static method named version which takes in the path and returns the hashed file name. Here we use leverage the revision manifest to look for the updated hash file name. We also use runtime cache to avoid having to read the manifest for every request.

    public static class Revision
    {
        public static string Version(string path)
        {
            if (HttpRuntime.Cache[path] == null)
            {
                using (StreamReader sr = new StreamReader(HostingEnvironment.MapPath("~/Scripts/rev-manifest.json")))
                {
                    Dictionary<string, string> rev = JsonConvert.DeserializeObject<Dictionary<string, string>>(sr.ReadToEnd());
                    string revedFile = rev.Where(s => path.Contains(s.Key)).Select(g => g.Value).FirstOrDefault();
                    string actualPath = "/Scripts/dist/" + revedFile;
                    HttpRuntime.Cache.Insert(path, actualPath);
                }
            }

            return HttpRuntime.Cache[path] as string;
        }
    }

Now, we can use the above method in our razor view like so.

<link rel="stylesheet" href="@MyNamespace.Revision.Version("~/Scripts/dist/assets/css/site.compiled.css")">;