This section is based heavily on the excellent RequireJS documentation at http://requirejs.org/docs/jquery.html, and is used with the permission of RequireJS author James Burke.
When a project reaches a certain size, managing the script modules for a project starts to get tricky. You need to be sure to sequence the scripts in the right order, and you need to start seriously thinking about combining scripts together into a bundle for deployment, so that only one or a very small number of requests are made to load the scripts. You may also want to load code on the fly, after page load.
RequireJS, a dependency management tool by James Burke, can help you manage the script modules, load them in the right order, and make it easy to combine the scripts later via the RequireJS optimization tool without needing to change your markup. It also gives you an easy way to load scripts after the page has loaded, allowing you to spread out the download size over time.
RequireJS has a module system that lets you define well-scoped modules, but you do not have to follow that system to get the benefits of dependency management and build-time optimizations. Over time, if you start to create more modular code that needs to be reused in a few places, the module format for RequireJS makes it easy to write encapsulated code that can be loaded on the fly. It can grow with you, particularly if you want to incorporate internationalization (i18n) string bundles, to localize your project for different languages, or load some HTML strings and make sure those strings are available before executing code, or even use JSONP services as dependencies.
The easiest way to use RequireJS with jQuery is to download a build of jQuery that has RequireJS built in. This build excludes portions of RequireJS that duplicate jQuery functionality. You may also find it useful to download a sample jQuery project that uses RequireJS.
Using RequireJS in your page is simple: just include the jQuery
that has RequireJS built in, then require your application files. The
following example assumes that the jQuery build, and your other scripts,
are all in a scripts/
directory.
Example 10.5. Using RequireJS: A simple example
<!DOCTYPE html> <html> <head> <title>jQuery+RequireJS Sample Page</title> <script src="scripts/require-jquery.js"></script> <script>require(["app"]);</script> </head> <body> <h1>jQuery+RequireJS Sample Page</h1> </body> </html>
The call to require(["app"])
tells RequireJS to load
the scripts/app.js
file. RequireJS will load any
dependency that is passed to require()
without a
.js
extension from the same directory as
require-jquery.js
, though this can be configured to
behave differently. If you feel more comfortable specifying the whole
path, you can also do the following:
<script>require(["scripts/app.js"]);</script>
What is in app.js
? Another call to
require.js
to load all the scripts you need and any
init work you want to do for the page. This example
app.js
script loads two plugins,
jquery.alpha.js
and
jquery.beta.js
(not the names of real plugins, just
an example). The plugins should be in the same directory as
require-jquery.js
:
Example 10.6. A simple JavaScript file with dependencies
require(["jquery.alpha", "jquery.beta"], function() { //the jquery.alpha.js and jquery.beta.js plugins have been loaded. $(function() { $('body').alpha().beta(); }); });
RequireJS makes it easy to define reusable modules via
require.def()
. A RequireJS module can have dependencies
that can be used to define a module, and a RequireJS module can return a
value — an object, a function, whatever — that can then be consumed by
yet other modules.
If your module does not have any dependencies, then just specify
the name of the module as the first argument to
require.def()
. The second argument is just an object
literal that defines the module's properties. For example:
Example 10.7. Defining a RequireJS module that has no dependencies
require.def("my/simpleshirt", { color: "black", size: "unisize" } );
This example would be stored in a my/simpleshirt.js file.
If your module has dependencies, you can specify the
dependencies as the second argument to require.def()
(as
an array) and then pass a function as the third argument. The function
will be called to define the module once all dependencies have loaded.
The function receives the values returned by the dependencies as its
arguments (in the same order they were required in the array), and the
function should return an object that defines the module.
Example 10.8. Defining a RequireJS module with dependencies
require.def("my/shirt", ["my/cart", "my/inventory"], function(cart, inventory) { //return an object to define the "my/shirt" module. return { color: "blue", size: "large" addToCart: function() { inventory.decrement(this); cart.add(this); } } } );
In this example, a my/shirt module is created. It depends on my/cart and my/inventory. On disk, the files are structured like this:
my/cart.js my/inventory.js my/shirt.js
The function that defines my/shirt
is not called
until the my/cart
and my/inventory
modules
have been loaded, and the function receives the modules as the
cart
and inventory
arguments. The order of
the function arguments must match the order in which the dependencies
were required in the dependencies array. The object returned by the
function call defines the my/shirt
module. Be defining
modules in this way, my/shirt
does not exist as a global
object. Modules that define globals are explicitly discouraged, so
multiple versions of a module can exist in a page at a time.
Modules do not have to return objects; any valid return value from a function is allowed.
Example 10.9. Defining a RequireJS module that returns a function
require.def("my/title", ["my/dependency1", "my/dependency2"], function(dep1, dep2) { //return a function to define "my/title". It gets or sets //the window title. return function(title) { return title ? (window.title = title) : window.title; } } );
Only one module should be required per JavaScript file.
Once you incorporate RequireJS for dependency management, your
page is set up to be optimized very easily. Download the RequireJS
source and place it anywhere you like, preferrably somewhere outside
your web development area. For the purposes of this example, the
RequireJS source is placed as a sibling to the
webapp
directory, which contains the HTML page and
the scripts directory with all the scripts. Complete directory
structure:
requirejs/ (used for the build tools) webapp/app.html webapp/scripts/app.js webapp/scripts/require-jquery.js webapp/scripts/jquery.alpha.js webapp/scripts/jquery.beta.js
Then, in the scripts directory that has
require-jquery.js
and app.js, create a file called
app.build.js with the following contents:
Example 10.10. A RequireJS build configuration file
{ appDir: "../", baseUrl: "scripts/", dir: "../../webapp-build", //Comment out the optimize line if you want //the code minified by Closure Compiler using //the "simple" optimizations mode optimize: "none", modules: [ { name: "app" } ] }
To use the build tool, you need Java 6 installed. Closure Compiler
is used for the JavaScript minification step (if optimize:
"none"
is commented out), and it requires Java 6.
To start the build, go to the webapp/scripts directory, execute the following command:
# non-windows systems ../../requirejs/build/build.sh app.build.js # windows systems ..\..\requirejs\build\build.bat app.build.js
Now, in the webapp-build directory, app.js
will have the app.js
contents,
jquery.alpha.js
and
jquery.beta.js
inlined. If you then load the
app.html
file in the
webapp-build
directory, you should not see any
network requests for jquery.alpha.js
and
jquery.beta.js
.
Copyright Rebecca Murphey, released under the Creative Commons Attribution-Share Alike 3.0 United States license.