The KIRUPA orange logo! A stylized orange made to look like a glass of orange juice! Tutorials Books Videos Forums

Change the theme! Search!
Rambo ftw!

Customize Theme


Color

Background


Done
Look, it's a kirupa!!!

Managing Dependencies with RequireJS

by Millad Dagdoni   |   5 June 2013

Large web applications are often built using a large number of JavaScript files. It isn’t uncommon to have applications with tens or even hundreds of JS files that are loaded manually by the application. When you are dealing with applications that contain that many files, how well you organize the files is important. This is important both from helping maintain your code, but it is also important to ensure the right code is loaded at the right time to ensure your users’ browser isn’t wasting time doing unnecessary work.

To help with organizing your JS files, this tutorial will introduce you to the popular RequireJS library and Asynchronous Module Definition (AMD for short).

Making a Case for Organization

Take a look at the following code:

<script src="script1.js"></script>
<script src="script2.js"></script>
<script src="script3.js"></script>
<script src="script4.js"></script>
<script src="script5.js"></script>
<script src="script6.js"></script>
<script src="script7.js"></script>
<script src="script8.js"></script>
<script src="HomePageController.js"></script>
<script src="AboutPageController.js"></script>
<script src="AppController.js"></script>
<script src="Main.js"></script>

What you see is typical of how script files are manually loaded in a large web application. While this approach may be acceptable when dealing with a small number of files, by using this method for loading scripts on anything even slightly complex, you can very easily end up with a pile of spaghetti code - code that is very disorganized and difficult to follow:

messy wires

( image is from Sam Dutton's presentation on web apps )

What you need is a way of managing these relationships between the files - a daunting task in JavaScript.

What is one way of making this task less daunting? Well...what if we let each JavaScript file declare its own dependencies so that you don’t have to do all of this? If you are in violent agreement at this, all we need is a library that can load dependencies for us, and for each JavaScript file, treat those files as modules with many dependencies and sub dependencies. We need a library that can wrap units of code in your application into manageable and testable modules. If the title of this article didn't give it away already, Require.js is that library.

Organization with Modules

JavaScript doesn’t provide a easy way for us to write modular web applications. Many web developers are left with normal scripts that are attached together in the DOM with namespaces in a single global object.

Before I continue further, let’s first define what I mean by modular. When we say an application is modular, we generally mean it’s composed of a set of highly decoupled, different pieces of functionality stored in JavaScript module files. This helps the developer build modules that are loosely coupled. What this means is that each component or module can operate or be tested independently of other components. Loose coupling makes our web application code easier to maintain and test. Loose coupling makes web applications easier to maintain by removing dependencies where possible. When this is implemented efficiently, it’s quite easy to see how changes to one part of a system may affect another.

This is where Asynchronous Module Definition (AMD) comes in. It provides a solution for defining modules so that the module and its dependencies can be asynchronously loaded. The module concept was greatly used by Common.js in NodeJS as it solved how to load modules based on dependency definition. AMD was born to take the module and dependency definition API from the server side to the client side. Require.js is compatible with the AMD (Asynchronous Module Definition) format, a format which was born to write something better than the old write lots of JavaScript tags in the browser approach.

How Require.js Works

Now that you have an idea of why defining modules is important, let’s take a look at how such a module definition looks like using Require.js. This will partly be academic for now, but you’ll put this knowledge to good use in the next section where you will actually use it for a real example. Require.js provides a method called "define" that is used for defining modules, it also provides a method called “require” to load any other normal JavaScript files that you need:

define(id, dependencies, factory);

Let’s briefly look at the arguments the define method takes.

Looking at an Example

Require.js, at its core, is a simple JavaScript file that can be included in a website or a web application to add JavaScript AMD functionality, and provides good application design. Require.js is a very clever library that takes your module dependency map, loads JavaScript modules and normal JavaScript files, and executes modules in order.

Let's look an example using a car as our complex web application:

this looks really simple, right?

We are going to demonstrate the use of modules. Like a real car, a car has some of the following dependencies: body, wheels, doors, engine, etc. Your web application is the car and its parts are your modules that make up your web application.

Let's look at the code below:

define("car",  
	["parts/body", "parts/wheel", "parts/door","parts/engine"],
	function(body, wheel, door, engine) {
		var Car = function() {
			this.engine = engine;
			this.wheel = wheel;
			this.door = door;
			this.body = body;
			this.horn = function() {
				console.log("Car tuuut tuuut");
			}
		} 
		return Car;
 });

As shown in the code, we are defining a module called car:

define("car",  
	["parts/body", "parts/wheel", "parts/door","parts/engine"],
	function(body, wheel, door, engine) {
		var Car = function() {
			this.engine = engine;
			this.wheel = wheel;
			this.door = door;
			this.body = body;
			this.horn = function() {
				console.log("Car tuuut tuuut");
			}
		} 
		return Car;
 });

Next, we have our dependencies:

define("car",  
	["parts/body", "parts/wheel", "parts/door","parts/engine"],
	function(body, wheel, door, engine) {
		var Car = function() {
			this.engine = engine;
			this.wheel = wheel;
			this.door = door;
			this.body = body;
			this.horn = function() {
				console.log("Car tuuut tuuut");
			}
		} 
		return Car;
 });

Our dependencies are the modules called body, wheel, door, and engine. They all live inside the parts folder, so notice how we designate that as part of defining these modules.

The last thing we have is the function that gets called once our modules have loaded:

define("car",  
	["parts/body", "parts/wheel", "parts/door","parts/engine"],
	function(body, wheel, door, engine) {
		var Car = function() {
			this.engine = engine;
			this.wheel = wheel;
			this.door = door;
			this.body = body;
			this.horn = function() {
				console.log("Car tuuut tuuut");
			}
		} 
		return Car;
 });

Now that we've looked at how we defined the car module and its dependencies, let's declare a few dependencies as well. The dependencies we will declare will be for wheel and engine.

Inside the parts folder, we have two JS files called wheel.js and engine.js. Notice how the name of these files matches the name of the modules you defined earlier. Inside wheel.js, you would define the module more formally:

define({
	wheelsCount: 4
});

Notice that this definition is very simple. We bypassed specifying an id or the dependencies and went straight to the function that will get called. The contents of wheel.js are pretty similar as well:

define({
	engineCylinder: "V8"
});

You can do something similar for the body and door dependencies as well. The end result is that when your require.js library runs, when it encounters the car definition, it will load the appropriate values from the modules without you having to do anything extra. All you have to do is simply "require" the Car module:

require(["Car"],function(Car){
    var car = new Car(); 
    car.horn(); 
});

There is a fair amount of bookkeeping-related tasks such as the proper file and folder management that I am glossing over here, but that is the gist of what you need to do.

Conclusion

I hope by now you have learned a bit about Require.js and how it can help simplify working with complex applications. The Require.js website is a great place to start learning and using this great library.

Millad Dagdoni

 

The KIRUPA Newsletter

Thought provoking content that lives at the intersection of design 🎨, development 🤖, and business 💰 - delivered weekly to over a bazillion subscribers!

SUBSCRIBE NOW

Creating engaging and entertaining content for designers and developers since 1998.

Follow:

Popular

Loose Ends