Wednesday 19th of August 2015
Until recently, I’d mostly avoided RequireJS under the mistaken impression that it was overly complicated for my needs and only useful for large-scale applications. I was mistaken: it’s actually a fairly straightforward thing that can work just fine for small apps, and more importantly it encourages an organised and structured approach to JavaScript code.
The RequireJS documentation is not, in my opinion, super helpful for beginners. It feels like it’s written for really smart people who can infer everything from minimal information, and doesn’t do a great job of explaining the basic concepts behind RequireJS, AMD etc. Who knows, maybe I’m just not that clever. Anyway, this article is intended to help folks struggling with these basics.
What is it for?
The purpose of RequireJS is to allow you to split your JavaScript code into separate files, called modules. Each of these files contains code to handle a well-defined aspect of your application, by exporting an object that other modules can use.
The first main aspect of this purpose is satisfying dependencies. A simple example is using jQuery as a module:
// simple_application.js
requirejs(['jquery'], function ($) {
var $images = $('img');
var imageData = [];
$images.each(function () {
imageData.push({
url: this.src,
width: this.naturalWidth,
height: this.naturalHeight
});
});
console.log(imageData);
});
The first parameter to requirejs()
is the list of dependencies that must be satified, in order, before the second parameter, a function, is invoked with the objects each of those modules exports as arguments. In other words, you say what you want, and RequireJS loads those things and calls your function with them as parameters.
The second main aspect of RequireJS is allowing you to define your own modules. Here’s a simple example:
// image_scraper.js
define(['jquery'], function ($) {
var scrape = function () {
var $images = $('img');
var imageData = [];
$images.each(function () {
imageData.push({
url: this.src,
width: this.naturalWidth,
height: this.naturalHeight
});
});
return imageData;
};
// Export the scrape function. This is what users of our module see.
return scrape;
});
The important part here is the return scrape;
line – the return value of a define()
function parameter is what is exported, i.e. made available to code that requires that module; in this example we export the scrape()
function. We can now require this module in another file:
// simple_application.js
requirejs(['image_scraper'], function (getImageData) {
console.log(getImageData());
});
Note how we only need to specify image_scraper
as a dependency. That module itself already specified jquery
as a dependency, so RequireJS does all the work of loading jQuery first before loading and executing our module. Furthermore, you can see that we can use whatever name we like for the object inside our function.
How code gets loaded
You could, in very simple cases, just have the following in your HTML and the above examples would work fine:
<script src="require.js"></script>
<script src="simple_application.js"></script>
But of course this assumes your JavaScript files are in the same place as your HTML, which is usually not the case – they could be in /assets
, for example, or /public/javascript
, or even on a different server entirely. Unless told otherwise, RequireJS will assume the module names you specify as dependencies match the path and filename of the JavaScript files where those modules are defined.
In any non-trivial situation, you’ll need to specify some configuration options:
<script src="/static/javascript/require.js"></script>
<script>
require.config({
baseUrl: "/static/javascript",
paths: {
underscore: "lib/underscore.min",
jquery: "http://code.jquery.com/jquery-1.11.3.min.js"
}
});
require(['simple_application']);
The baseUrl
option is pretty self-explanatory: it tells RequireJS where our JavaScript files are, so when we specify some_module
as a dependency, it knows to fetch the file from /static/javascript/some_module.js
.
The paths
option is handy because it lets us specify mappings of simple module names to more complex path and filenames, or even full URLs. In this example, we tell RequireJS that when we ask for underscore, we mean the file /static/javascript/lib/underscore.min.js
, and that jQuery should be loaded from the jQuery.com CDN.
The call to require()
on the last line in the example is our application’s entry point, i.e. what gets the ball rolling: it loads and executes /static/javascript/simple_application.js
and any/all of its dependencies.
That’s it, really
That should explain how to get going with RequireJS, and while it’s fairly straightforward, it can have some far-reaching consequences for how you think about structuring code.
There’s certainly more to learn about RequireJS, in particular optimisation, where you use command-line tools to build a single concatenated and minified JavaScript file for use in production environments. Hopefully, though, this article is enough to get you started, and build your confidence so that you can tackle more advanced aspects yourself.
At some point I also want to write about Backbone, for similar reasons to why I wrote about RequireJS: it looks more complicated than it really is, and it’s a great help when writing well-organised code. It also works really well, conceptually, with RequireJS.
No comments
Write a comment: