Sponsored by

  • Intel
  • HP

JavaScriptTutorial

Shrink JavaScript files with Google Closure Compiler

Reducing the size of your JavaScript code with Google Closure Compiler will give your site a huge speed boost, explains Matt Kelly of ZURB

Bandwidth and browser technology are improving to meet the demands of highly JavaScripted applications, but you still need to be concerned with the size of your JavaScript code.

There are two main reasons why size is important. First, even if the JavaScript you serve is cached, it still needs to be evaluated by the browser on every request. While other loading actions, such as downloading assets, can be run in parallel, JavaScript evaluation can’t. This means that the size of your JavaScript files will have a larger impact on page load time than the size of CSS files or images. Reducing the size of your JavaScript is the only way to reduce the amount of time the browser spends evaluating it.

Advertisement

Second, you want your users’ first impressions of your site to be snappy ones. This is always going to be on an unprimed cache, meaning that all your JavaScript needs to be downloaded before the page loads. More JavaScript means more time waiting for the page to load. Do the maths: less JavaScript is better. Enter Google Closure Compiler, a tool designed to make your JavaScript more efficient. You’ll find all the documentation at code.google.com/closure/compiler.

The range of Closure Tools available from Google include Compiler, for minifying and checking JavaScript, and the comprehensive, cross-browser Closure Library
Streamlining your code

So, you’re convinced that less JavaScript means faster pages, but how do you reduce the amount of JavaScript without removing functionality? Existing JavaScript minifiers do a fine job of reducing the file size of JavaScript, which will help with unprimed caches. However, the amount of functional JavaScript that needs to be evaluated is unchanged. That is to say, we still have the same number of functions, loops and variables.

Decreasing the amount of time the browser spends evaluating JavaScript is only accomplished by reducing the actual quantity of functional JavaScript on the page. Your two options to accomplish this are to break up your JavaScript manually into small libraries that can be included selectively on each page, or to include all the libraries you want and have the Closure Compiler automatically remove anything you don’t end up using when you’re ready to publish your application.

If the former sounds like fun then you needn’t read any further in this article – your deep love for micromanagement will suit you better in a career as a middle manager or professional StarCraft player. For everyone else, let’s dive into the Closure Compiler.

The Closure Compiler has three different levels of compression. The first two, Whitespace Only and Simple Optimizations, are similar to other JavaScript minifiers in that they only make changes that the compiler is certain won’t change functionality. Advanced is much more aggressive, removing unused code and renaming functions, so we need to make sure our code is written in a way that can’t be misinterpreted. The Advanced mode is by far the most interesting and the rest of this article will deal solely with that mode.

Declaring jQuery as an extern

When we compile our code with Advanced settings, almost everything is going to be renamed by the compiler, so it’s required that all the JavaScripts we’ll be using on the page are compiled together. However, there are some cases where this just isn’t practical. If we’re using libraries that we can’t (or choose not to) modify to work with Advanced Optimizations, then we need to tell Closure that this library is external.

If a library is specified as external, Closure won’t modify this code and won’t rename any references to its functions or objects. This is the ideal choice for compiling jQuery code because jQuery itself doesn’t yet compile with Advanced Optimizations. Also, referencing an unmodified copy of jQuery from our code enables us to link jQuery from one of the publicly available content delivery networks (CDNs).

It’s important to note that declaring a library as an external means users will have to download it in its entirety.

Once your code is compiled, there's a chance you'll see lots of warnings, but for this tutorial you're safe to ignore them - so long as there aren't any errors

If the majority of the JavaScript you’re serving is contained in libraries identified as externals then you’re not going to see a very large reduction in overall file size compared to standard minification techniques.

Also note that external libraries should still be minified with the Closure Compiler’s Simple Optimizations or any other minifier. The real benefit comes when you have large amounts of JavaScript for your entire application but don’t use all of it on every page. Or perhaps you have a large library of jQuery plug-ins that you use across many applications. Compiling with Closure enables you simply to link the entire library and allow the compiler to remove whatever you don’t use for the page.

Use string literals for events

Even when declaring jQuery as an external library, there are still some issues that may arise when referencing jQuery from compiled code. Most noteworthy of these is that jQuery’s convenience event methods (click, mouseover and so on) are still renamed in your code by the compiler, even though jQuery was declared as an external.

Here's our JavaScript code before we began using the methods described in this tutorial for shrinking it down

The easiest solution to this is to use the bind method rather than the convenience methods:

 $(‘#sb’).bind(‘click’, function (event) {     $(‘#su’).fadeIn();   });

Another problem you’re going to run into is quoted string properties versus dot syntax properties. Quoted string properties are object properties referenced with a string literal:

 response[‘status_code’] = 200;

As opposed to properties referenced with the dot syntax:

alert(response.status_code);

As a rule, the compiler will not rename string literals, so the previous two lines of code will effectively be compiled to the following:

a[‘status_code’] = 200; alert(a.b);
The results speak for themselves. We've reduced our code substantially, and thus sped up our site - which is great news for users!

The result is that b is undefined when we expected it to be 200. The best way to avoid this is to always use the dot notation for properties used within your code, and use quoted string for properties that are read or sent to other libraries. Our previous examples could be rewritten as:

response.statusCode = 200;  alert(response.statusCode);  

and would compile to:

 a.b = 200;  alert(a.b);  

When making requests to external APIs, the string literal must be used so that the correct property is referenced. For example, the following:

 function ajaxCallback(response) { $(‘#su’).val(response.status_code === 200 ? response.data.url : ‘Bad Request’); }

will compile to:

function a(b) {      $(‘#su’).val(b.c === 200 ? b.d.e : ‘Bad Request’);  }
The source map pane will indicate that the mapping file was loaded. Next, select your compiled JavaScript file in the left pane of the Script tab in FireBug

The properties c and d are undefined so Bad Request will always be set. The correct way to write this is:

function ajaxCallback(response) { $(‘#su’).val(response[‘status_code’] === 200 ? response[‘data’][‘url’] : ‘Bad Request’);  }

which compiles to:

function a(b) { $(‘#su’).val(b[‘status_code’] === 200 ?b[‘data’][‘url’] : ‘Bad Request’); }

The same applies when you’re sending data to an API or external library. Take, for example, the request data for the previous code:

var data = {url: ‘http://zurb.com’, login: ‘zurb’};

Compiled, it looks like this:

var a = {b: ‘http://zurb.com’, c: ‘zurb’};

The API is expecting the URL and login parameters and not b and c. Use the string literals instead:

var data = {‘url’: ‘http://zurb.com’, ‘login’: ‘zurb’}; Using the command line tool

Once you’ve got all your object properties consistent, it’s time to compile your code. You’re going to need Java Runtime version 1.6 or greater, the Closure Compiler jar, and a local copy of the jQuery version you’re using.

 

Assuming these files are all in the same directory, you can now run the following command

 java -jar compiler.jar --compilation_level ADVANCED_OPTIMIZATIONS --js uncompressed1.js – -js uncompressed2.js --externs jquery-1.4.2.min.js -- js_output_file compressed.js
Quickly discover the origins of a piece of JavaScript. Here, we can see that our selected code was found in line 63 in the source file application.js

The jar option specifies the path to the Closure Compiler jar file. The js option indicates the js files to be compressed: it can be included more than once to compile many JavaScript files into a single compiled file. Externs indicates that jQuery is an external library: this option can also be repeated for multiple externs. Js output file indicates where the compiled files should be written.

Compiling your code can (and most likely will) produce an extensive list of warnings. Explaining what they mean is beyond the scope of this article, so for our purposes as long as you don’t have any errors, you’re good to go.

A full list of options can be displayed by running:

 java -jar compiler.jar –-help

Now that your code has been compiled, it’s time for you to test it and make sure everything still functions as expected. More specifically, you’ll want to check for function-missing exceptions, occurring as a result of code being incorrectly removed, or more subtle errors that result from inconsistent property naming.

Debugging the code

As you can imagine, debugging compiled code is about as fun as browser testing IE5 (“What do you mean function ‘r’ is undefined?”). However, if you keep your properties consistent and compile all your code together, you should avoid any functional differences between compiled and uncompiled code.

If something is broken, though, don’t panic and freak out quite yet. There’s a Firefox plug-in that will assist you in mapping the renamed functions to their original, more meaningful, names.

By right-clicking Show Original Source, you can view a selected piece of code as it appeared in the uncompiled JavaScript, making it easier to track down bugs

Start by installing FireBug (if you don’t already have it on your machine) and the Closure Inspector Firefox add-ons. Now we need to recompile our JavaScript with one more option:

 --create_source_map ./source-map

This creates a file that maps the original source code to the compiled source code and can be read by the Closure Inspector.

Open the page that uses your compiled JavaScript in Firefox and open FireBug. Click the Script tab in FireBug and then select the Source Mapping tab in the right pane. Click Open Local File in the source mapping pane and select the source map that was created when you ran the Compiler. The source map pane will indicate that the mapping file was loaded.

Now select your compiled JavaScript file in the left pane of the Script tab in FireBug. The code is still unreadable, but by right-clicking and selecting Show Original Source you can determine the file and line number in the uncompiled JavaScript from which the selected code was generated.

It may not seem like much, but being able to find the original names and context makes debugging a lot easier. Note that, when doing this for the first time, you may be asked to select the location of the source code; in the example given here, that’s going to be the directory from which you ran the Compiler command.

Managing your workflow

If you don’t want to have to run the Compiler tool manually every time you make an update to your JavaScript, it would be wise to integrate this step with your deployment scripts.

If you’re using Ruby on Rails, use this plug-in, which will add Google Closure compilation to the asset caching that’s already built into Rails: github.com/mkelly12/google_closure_compiler.

The Jammit Ruby gem also has support for the Closure Compiler and it’s worth checking out.

Advert

Log in to Creative Bloq with your preferred social network to comment

OR

Log in with your Creative Bloq account

site stat collection