Contact

Technology

Aug 27, 2014

Modern Web Development Best Practices Powered by Grunt.js Part 7: Caching

Fernando Berrios

Fernando Berrios

Default image background

In the previous parts of this series, we focused on improving the development process. From here on out, we will go over a few tips and tricks that will make your life easier when deploying your application to production. Some of these will help with performance, while others will be about improved maintenance and an overall better experience for your users.

Cache

Let’s start with one of the most basic but important ones. Caching is an extremely important area of software development, one that will have a huge impact on the performance of your application. On the server side, caching can mean a lot of things. Depending on what type of caching and what you are caching, it can range from a quick fix to a very hard implementation. On the front-end side, caching can be a bit easier to implement. In this example we’ll go over some of the low hanging fruit that I have found is often completely forgotten: caching static assets (e.g., CSS style sheets, JS files, images, etc.) in the browser.

Why bother with asset caching? Doesn’t the browser take care of all that? Correct, the browser does cache static assets very efficiently and in some cases too efficiently. We start caring about caching and controlling it in situations where a deployed app is getting an update, a hotfix, or even something as simple as a promo image update. Without cache controls on updated assets, you can run into issues where a user visits the updated site and stuff doesn’t work as expected or look right. This can lead to problems that are very hard to debug.

Tutorial:

git checkout usemin-rev npm install

Controlling the caching of static assets in the browsers can be achieved in a few ways, the most common being HTTP headers, query string parameters, and versioned file names. HTTP headers are powerful and flexible. They don’t require explicit changes to the codebase but are usually complex to implement (requiring control over the web server) and hard to integrate with a build or deployment process. Query string and versioned file names are easier to integrate but require a bit of legwork. Here are some examples of query string caching that I’ve seen used:

<script src="js/app.js?t={timestamp}"></script>

<link rel="stylesheet" href="stylesheets/screen.css?v={version}" />

A query string with a version number or timestamp (usually of when it was built) will work to bust the browser cache. It works because the browser will treat it as a different file (e.g., “app.js?v=1.1” is not the same file as “app.js?v=1.2”). This approach is also fairly easy to implement with a bit of back-end logic or a powerful build tool, and it gets most of the job done. The problem is that most caching proxy systems ignore query strings and see them as the same file. Caching proxies are systems that sit between the server and client in some networks. They each have a different level of caching, depending on how they are configured. But regardless of that, you want to eliminate any possibility that an incorrect version of your assets gets sent to your user. To overcome that issue, the versioned file name (or version folder name) approach works best. Here are a few examples:

<script src="js/app.{timestamp}.js”><script> <script src="js/{timestamp}/app.js"></script>

<link rel="stylesheet" href="stylesheets/{unique\_hash}.screen.css" />

This approach meets all our caching needs, but usually it can be a bit of a challenge to set it up since it requires two things:

1.  A process that will create a copy of the file (or folder) with the appended token (a timestamp, hash, or version number).

2.  Something that will rewrite the includes in the HTML to point to those new files, or a back-end process that takes that generated token and fills it out in the appropriate places.

Doing either of those things tends to be a hacky process, but it can and should be done. Fortunately for us, there is an easy way of doing this with Grunt.js and a couple of plugins. One is the “usemin” plugin we used previously and the other is “grunt-rev,” which integrates well with “usemin,” All we need to do is modify our “Gruntfile.js” file by adding this object to the “grunt.initConfig” section:

rev: { files: [ 'js/app.js', 'stylesheets/*.css' ] }

Then we add the “rev” task to our “build-prod” task, right in between the “uglify” and the “usemin” call:

grunt.registerTask('build-prod', [ 'jshint', 'compass:clean', 'compass:prod', 'useminPrepare', 'concat', 'uglify', 'rev', 'usemin' ]);

When you run “grunt build-prod” you should see something like this:

These tokens you see highlighted above are unique hashes generated from the content of the files, which means the hash changes only when there has been a change inside of it. When the files are deployed, only the updated assets will invalidate their cached version—the rest won’t need to be re-fetched.

In the rest of this series, we’ll go over some other advanced concepts like application versioning, image optimization, and a few other tricks. We’ll also see how to use more Grunt.js features and plugins and how to implement them.  In the meantime, follow @CrederaMSFT on Twitter and Credera on LinkedIn for more great best practices.

To view the rest of the Modern Web Development Best Practices Powered by Grunt.js series click here.

Conversation Icon

Contact Us

Ready to achieve your vision? We're here to help.

We'd love to start a conversation. Fill out the form and we'll connect you with the right person.

Searching for a new career?

View job openings