Digital Development
Web Design

With the trend of web sites turning into web apps the complexity of javascript code is grows as the site gets bigger, leading to more complex file assembly. As a developer its a lot easier to work with discrete JS files/modules, however, when deploying your site/app its best to use optimized code in just one or a few HTTP calls. Require.js is a javascript library which allows you to write your script in discrete modules while at the same time creating as few HTTP requests as possible.

Asynchronous Module Definitions

So how does Require achieve this? Require.js is built around Asynchronous Module Definitions (AMD). AMD is designed to load modular code asynchronously in the browser and server. Its a fork of the Common.js specification. The AMD API specifies a mechanism for defining modules such that the module and its dependencies can be asynchronously loaded. This is particularly well suited for the browser environment where synchronous loading of modules incurs performance, usability, debugging, and cross-domain access problems.

Require.js  has become the AMD script loader of choice for many as it has a great community and it is growing rapidly. The main reasons for its popularity are its optimization for in-browser use, as well as its use in other JavaScript environments, e.g. Node. Using Require.js, you will load only the relevant module dependencies in their right order.

How Exactly Does It Work?

Require.js creates script tags for each dependency you defined and loads those dependencies on-the-fly using the head.appendChild() function. After script module loading, the modules are then ordered and called in the specified order. Thus, you only need one root to load the entire site/app functionality that you need and Require.js  does the rest.

Require.js and jQuery - Single Page Microsite

Require.js loads jQuery just like any other dependency, however, you're likely have other scripts in your project that also depend on jQuery. This tutorial will focus on setting up Require.js for a single-page microsite which utilizes jQuery and jQuery-dependent scripts. The file structure from the project is as follows:

htdocs/
---tools/
------build.js
---------r.js
------www/
---------app.html
---------js/
------------app.js
---------lib/
------------jquery.js
------------jquery.alpha.js
------------jquery.beta.js
------------require.js
---------app/
------------main.js
------------other.js

The following line of code is included in inline script tags your app.html file in order to call your sripts and replaces the traditional list of script calls in your source code:

data-main="js/app" src="js/lib/require.js"

Setting Up Your Require.js Config File

The Require.js config file is app.js and will be called in your app.html file. Within app.js set the baseUrl to be the directory for third-party, library code, js/lib.  The paths config is then set for your custom app code. As we're using jQuery we'll use the shim config (from the Require.js API) to specify dependencies for jQuery plugins that do not call define(). This approach is useful if you have an existing jQuery project you want to convert and do not want to modify the sources of the jQuery plugins to call define(). Finally, we load the main app module to start the app.

requirejs.config({
"baseUrl": "js/lib",
"paths": { "app": "../app" },
"shim": { "jquery.alpha": ["jquery"], "jquery.beta": ["jquery"], "jquery.isotope": ["jquery", "jquery.beta"] }
});
requirejs(["app/main", "app/other"]);

Load any of your custom script logic in the app/main file, or other app/ files as required. Its recommended practice to keep your script modular, separating your script logically to enable improved performance, usability, debugging, and cross-domain access. In the following example, the function makes use of alpha() and beta(), functions which have been defined in jquery.alpha and jquery.beta, respectively, thus, these files must be defined before the function is declared. As both jquery.alpha and jquery.beta are both dependent on jquery we also define jquery here.

define(["jquery", "jquery.alpha", "jquery.beta"], function($) {
$(function() {
$('body').alpha().beta();
});
});
define(["jquery"], function($) {
$(function() {
$('body').append('Hi, other.js is loaded!');
});
});

Build & Optimise

RequireJS has an optimization tool that:

Combines related scripts together into build layers and minifies them via UglifyJS (the default) Optimizes CSS by inlining CSS files referenced by @import and removing comments.

The optimizer is part of the r.js adapter for Node and Rhino, and it is designed to be run as part of a build or packaging step after you are done with development and are ready to deploy the code for your users. For command line use, Node is the preferred execution environment. The optimizer runs much faster with Node. Once Node is installed locally you can install require.js and the r.js optimiser. Save r.js in a directory that is a sibling to your project directory (see project structure above). The optimizer that is part of r.js can live anywhere you want, but you will likely need to adjust the paths accordingly.

You can either specify options on the command line or in a build profile. In build.js, the command line arguments can be specified as follows:

{
"appDir": "../www",
"baseUrl": "js/lib",
"dir": "../build",
"optimize": "none",
"mainConfigFile": "../www/js/app.js",
"modules": [
{
"name": "app"
}
]
}

This build profile tells RequireJS to copy all of /www to a sibling directory called /build and apply all the optimizations in the build directory. It is strongly suggested you use a different output directory than the source directory -- otherwise the optimizer overwrites your source. Require.js will use baseUrl to resolve the paths for any module names. The baseUrl should be relative to appDir. In the modules array, specify the module names that you want to optimize, in the example, "app". "app" will be mapped to www/js/lib/app.js in your project. The build system will then trace the dependencies for main.js and inject them into the build/js/lib/app.js file. It will also optimize any CSS files it finds inside build. To run the build, run this command from inside the tools directory:

node r.js -o build.js

Once the build is done, you can use  the build directory as your optimized project, ready for deployment.

In my next post I'll cover how you can use Require.js on multipage sites using jQuery with different jQuery-dependent scripts on each page.