In the previous section we discussed two simple approaches to organize your code into modules. But they have a major drawback: you have to manually manage the dependencies of each module. For example, a method of one module may need to use a property of another object. To avoid this issue you have to pay attention to the order in which you include them in your web pages, but for a project that has tens or hundreds of modules this becomes difficult and error-prone. What makes this solution even harder to employ is that some modules may depend on third-party plugins, libraries, or frameworks.
One method for solving this issue would be to define the dependencies of the modules and then have “something” that organizes the inclusions in the right order for you. This is where asynchronous module definition and RequireJS come into play.
The asynchronous module definition (AMD) is a JavaScript API that specifies a mechanism for defining modules so that the module and its dependencies can be asynchronously loaded.
RequireJS is a JavaScript file and module loader that’s optimized for in-browser use but that can be used in other JavaScript environments, like Rhino and Node.js. This library is highly configurable, allowing for a high level of flexibility, but it’s also possible to start simple to accommodate basic needs. In this section we won’t cover the library in detail, but we’ll describe it enough to get you started.
RequireJS loads each dependency as a script tag inside the head element of the page. Then the library waits for all dependencies to load and calculates the right order in which to call the functions that define the modules. Finally, it calls the module definition functions in the appropriate order.
Now that you know what RequireJS is and what it does, let’s start using it.
Getting started with RequireJS
Before you can use RequireJS in your web pages, we need to discuss some of its main concepts. The first topic we want to cover is the define() function defined by AMD.
Method syntax: define | |
---|---|
define([[id, ] dependencies,] factory) | |
Define a new module with optional dependencies and identifier. | |
Parameters | |
id | (String) The identifier of the module. |
dependencies | (Array) An array containing the name of the modules the new module depends on. |
factory | (Object|Function) An object literal or function that defines the new module. When a function is provided, it receives as parameters the dependencies in the order in which they are defined. |
Returns | |
undefined |
To fix the idea, let’s say that you have an object called Person, defined in a file called Person.js. Its only property is name and it has no dependencies. Using the define() function you can create it as such:
define({
name: 'John Doe'
});
As you can see, you declare neither an ID for this module nor dependencies.
In addition to Person, you have an object named Car that’s stored in a file called Car.js. This object has a method called getOwner() that internally uses the property name of the Person object (literal); thus it has the Person module as a dependency. The Car module can be defined as shown here:
define(['Person'], function(Person) {
function Car() {
this.getOwner = function() {
return 'The owner is ' + Person.name;
};
}
return Car;
});
In the code you include Person as a dependency and then create a function named Car that acts as a constructor. This function has a getOwner() method that returns a simple message that uses the name property of Person. Finally, Car is returned to be available as a module. So far, you’ve defined two modules, but you still haven’t used them. The require() function exists for such a purpose.
The require() function is similar to define() in that both define a module, but the former also executes it. This means that it loads and executes the dependent modules before executing the function provided. Usually an application has one require() function as a main entry and other modules defined via define().
To conclude our example, imagine you have a file called main.js acting as the entry point of your application, where you want to alert the name of the car’s owner. To do so, you can use require() defining Car as its dependency:
require(['Car'], function(Car) {
var car = new Car();
alert(car.getOwner());
});
If you’ve followed this section carefully, you should have a question blinking in your head: how does RequireJS know from a simple string (for example, "Car") what module to load? The answer is that the library creates a module having the same name as the file containing the definition. In the example, even though you didn’t define the ID for your modules, you have three module names: main, Car, and Person. The reason is that you have three JavaScript files: main.js, Car.js, and Person.js.
Let’s take this convention even further. If you have a file named Basket.js that’s stored in a folder called cart, the module will be named cart/Basket.
The final step to let RequireJS do its job is to include it in an HTML page. You can do so using a script element with the addition of a data-main attribute. This attribute is used to define the entry point of your application (the file using require()). Assuming that you’ve placed the RequireJS library and all the previously created modules in a folder called scripts, you could start the demo by writing
<script data-main="scripts/main" src="scripts/require.min.js"></script>
A working example that employs all the snippets we’ve developed in this section can be found in the folder lesson-15/requirejs.
Now that we’ve covered some basic concepts, let’s see how you can apply them and use RequireJS with jQuery.
Leave a Reply