Introduction to RequireJS
In this tutorial we are going to take a look at RequireJS, an AMD compatible asynchronous script loader that is incredibly powerful. In my experiments with RequireJS I've hugely enjoyed working with it and will be using it heavily in my future development. This is a fairly heavy post as far as complexity goes, but please do stick with it. I struggled to get my head around RequireJS and AMD for a long time but once it "clicks" it is really awesome.
The basic use case for RequireJS is as a basic script loader, but in this tutorial I wont concentrate on that, but on its uses for modular development. RequireJS implements the AMD (Asynchronous Module Definition) spec, which means we can write our own modules and load them with RequireJS, allowing it to manage dependencies for us. Have you ever had multiple script tags and had to load them in a particular order as one relied on the other? I have, and it's a nightmare. Working in a modular fashion really eliminates this issue and in this tutorial I hope to demonstrate just how.
To do this, we are going to build an app (sort of - it's all very basic snippets of code) that has dependencies. It depends on both Underscore and jQuery. We could just include this as a whole host of <script>
tags, but that's absolutely no fun and is also not efficient, when loading all those in a browser the rest of the page load will be blocked. We could minify them, but then we have to minify them and maintain order of the code, and it just becomes a nightmare. With RequireJS, we include the RequireJS source, and from there can get it to load in files.
Firstly, create your project directory and the structure within. Mine looks like this:
├── app.js ├── index.html ├── lib │ ├── modules │ │ └── template.js │ ├── require.js │ └── underscore.js
app.js
is my main file, we will look into this shortly.lib/modules
is where all my self-written modules will go. With RequireJS all our code gets split into modules. I'll explain further in a moment.- Files immediately within
lib
are external libraries, in this case the RequireJS source and also Underscore.
To get started, head into your index.html
file and add in this line:
That line loads in the RequireJS source, but also tells RequireJS to automatically load in app.js
. This is what I will refer to from now on as our "main" JS file, it's where we will put our configuration for RequireJS and load in code. This also sets the base path for loading in files, whenever we load in a file with RequireJS, it will treat the folder app.js
is within as the base path and load all files relative to that. Now we've got that done, we can get going.
Before I get ahead of myself, let me show you how we load in dependencies. This is done through the require
function. To load in some code to run after a script, you use it like so:
require(['myfile'], function(myFile) { myFile.init(); });
That would look for myfile.js
within the same directory as your main JS file, and whatever myfile
returns will be referenced within the callback as myFile
, as that's the variable name I passed into the callback. With libraries like jQuery and Underscore that register global objects, you don't need to do this.
What we are going to do is set up jQuery with RequireJS. As of jQuery 1.7, it comes with support for AMD as it implements the AMD spec, so we can use it. You can see this right at the bottom of the un-minified source:
if ( typeof define === "function" && define.amd && define.amd.jQuery ) { define( "jquery", [], function () { return jQuery; } ); }
The define
function is implemented by RequireJS to allow us to define modules. This one defines a named module named "jquery". Usually when defining our own modules we don't explicitly name it (you'll see that later when we write our own) because the name is automatically generated by the file name, and we reference it based on that file name and the directory structure. Because jQuery has declared itself as a named module, we have to reference it as "jquery" when we load it in. This means, to make it work, we'd have to have the jQuery source within our main directory (alongside app.js
) and name it jquery.js
, so when we reference it within require()
as "jquery"
, it loads properly (remember that RequireJS doesn't care about .js on the end). However, I prefer to load my jQuery version in from the Google CDN, so I need some way of telling RequireJS that when I try to load "jquery", to fetch it from the CDN. Thankfully this is really easy:
require.config({ paths: { "jquery": "https://ajax.googleapis.com/ajax/libs/jquery/1.7.2/jquery.min" } });
That line means whenever I do:
require(['jquery'], function() { //some code });
It will pull in jQuery from the Google CDN. Note that I've removed ".js" from the end of the URL. We'll also be using Underscore, and to save typing lib/underscore
to load it in, I set up a path for that too (I tend to set up paths for most of my libraries I'm depending on. This means my config looks like:
require.config({ paths: { "jquery": "https://ajax.googleapis.com/ajax/libs/jquery/1.7.2/jquery.min", "underscore": "lib/underscore", } });
Now we have our set up sorted, lets write our first AMD module, called template.js
. This will provide a method that will compile a basic Underscore template and display it on the page. The functionality is very easy, as the idea here is to look more at the AMD side of things. To define a method, we use the define()
function. As we saw, we can explicitly name our module, like jQuery did, or we can let it be done based on the filename, which is fine. We need to pass define()
two things, an array of dependencies, and a function that will have our implementation in it. This module is going to depend on Underscore and jQuery:
define(['underscore', 'jquery'], function() {});
What we're going to do is write a function that will add a string to the body that says "Hello Name", but let the name be passed into the function. It's a really easy implementation:
var showName = function(n) { var temp = _.template("Hello <%= name %>"); $("body").html(temp({name: n})); };
All we do is create a basic Underscore template and compile it, passing in the name
variable. I then use jQuery to add it to the body
of the page. Nothing complex at all.
Now, to expose this method we simply need to return it. What we do is return an object containing properties that are the methods to expose. In our case:
return { showName: showName };
And with that, our entire module looks like so:
define(['underscore', 'jquery'], function() { var showName = function(n) { var temp = _.template("Hello <%= name %>"); $("body").html(temp({name: n})); }; return { showName: showName }; });
The great thing about this is that you can have functions in your modules that are useful for internal use but avoid exposing them, and by dividing your app into multiple modules it's a great way to organise your code.
Finally, all that's left to do is require our module in app.js
and then call showName()
on it:
require(['lib/modules/template'], function(template) { template.showName("Jack"); });
Here the module we're loading does not expose itself globally, so to get at whatever it returns, we pass in a variable to the callback function that will be bound to what our module returns. If you're loading multiple modules, add multiple variables. For example:
require(['moduleA', 'moduleB', 'moduleC'], function(a, b, c) {});
Once the module is loaded, I can call showName
and sure enough, I get "Hello Jack" in the browser if I refresh my index page.
Although this is a simple example I hope it helps to show the power behind RequireJS and what it can do with its modular approach. I've really enjoyed using it and will no doubt be exploring it further in future tutorials as it does plenty more stuff I haven't covered here.
As always, please do feel free to leave feedback and ask questions, I will endeavour to respond to them.