What do you want to learn?
Skip to main content
by Jeff Valore
Start CourseBookmarkAdd to Channel
Table of contents
Getting Started With RequireJS
Introduction to the Sample Project
Downloading and Including RequireJS
Defining and Requiring Modules
Using External Libraries as Dependencies
When we left our sample project, we were including RequireJS in the script tag and had a main.js file that included our big monolithic code for the to-do list. You'll remember that we were still loading jQuery using a script tag in the index.html file. jQuery is one of many libraries that is RequireJS aware, meaning that you can import it normally with a script tag, but you can also import it using RequireJS, and jQuery will recognize that RequireJS is loaded and register itself as a module that other modules can then be dependent upon. To load jQuery using require, we can remove the script tag from the HTML. Then we will need to tell RequireJS that we depend on jQuery. That way, it can go and load it for us. To get this magic to work, we need to add a call to require.config at the top of our main.js file. We're going to discuss configuration of RequireJS in depth in a later module. But for now, just know that jQuery registers itself as a module named jquery, all lower case. Here, we are telling RequireJS what file to load when we ask for the jQuery module. By default, RequireJS will add a .js extension, so we set the path to jquery-2.1.1.min since that is the name of the file without the extension. Again, you can just accept this as is for now. We'll come back to it in a later module and discuss it in much more detail in the module on configuring RequireJS. To specify what modules we're dependent on, RequireJS adds a function named require to the global scope. The require function is used to tell RequireJS that we need a set of dependencies before we can proceed. The first parameter to the function is an array of dependencies that we need loaded. These are specified as strings. Each string is the name of the module that we're dependent upon. The second parameter is a callback function. Since the module loading in RequireJS is asynchronous, we give RequireJS this function, and it will execute it once it is finished loading all of our dependencies. The callback function needs to have the same number of parameters as we have dependencies, but they don't have to have the same name. So, here we can depend on jQuery but pass it in as a parameter that is just the familiar dollar sign. RequireJS will call this callback function once it's done downloading all of our dependent modules, so the rest of our existing code just gets wrapped into this function. Here's the syntax for the require function that we just discussed and added to our main.js file. There are other parameters that we can pass to this function, but we'll discuss those later on in the course. Back in the web browser again, if we reload our application, you can see that it still functions properly. We're still able to add and remove tasks. You can also see in the Network tab of the dev tools that RequireJS now loaded jQuery for us. So, we're now loading jQuery as a dependency and injecting it into our application. However, we still have a lot of mixed responsibilities in our own code that need to be cleaned up. So, next, we need to properly break apart our own code into modules.
Asynchronous Module Definition
Throughout this course, I keep referring to modules. Modules are the basic building blocks of RequireJS. Each module can be dependent on other modules. This concept of modules and even the function names and syntax that RequireJS uses are not actually specific to require. RequireJS is what is known as an AMD module loader. AMD stands for Asynchronous Module Definition. The AMD API started as part of the CommonJS API, which was also in the process of defining module pattern. However, CommonJS was largely concerned with module loading outside the browser. In fact, CommonJS modules are what NodeJS currently uses to define its modules. The CommonJS API was difficult to implement in a browser environment, and so the CommonJS Transport/C proposal was introduced as a way to solve this. Eventually, CommonJS Transport/C became the AMD module API and is now maintained separate from CommonJS. The important thing to remember here is that we are defining AMD modules. This means that the modules you define for use in RequireJS can be loaded by other module loaders that are AMD compatible. For example, Curl.js is a popular alternative to Require. If you'd like to know more about the AMD specification, please see the URLs listed on this slide.
Defining AMD Modules
The Module Design Pattern
Define the Remaining Modules
In this section of the course, I'm just going to go ahead and break apart the rest of our existing sample application code into more modules. So, if you feel like you already have a good grasp on what we're doing with the modules and how Require is loading them, you can go ahead and skip this section. Otherwise, you can listen to me talk through refactoring some more of this code. So, here I have a set of things that work with creating DOM elements. That looks like a common responsibility, so I'm going to move those off to another module. I'll use my define function to define a module. I'll give it a name. Let's call it taskRenderer. I'll list anything that it depends upon, which for the moment is nothing, and give it a callback function that then takes no arguments since I'm depending on no other modules. We can get rid of this comment that no longer matters. And using the revealing module pattern, I can specify a return object here that will become my module. So, here again, I'm going to just use the revealing module pattern and return the functions that are defined above in an object. That module now needs to be added to the rest of our code just like we did with the taskData module by passing the module name to the dependency array list and to the function parameters. And then anything that was calling those functions before needs to now call them from the taskRenderer module. So, now we have our second module defined. Next, we have a lot of code here dealing with task management. So, let's also move those out of here. I'm just going to call this module tasks, and all the code that I just removed actually used taskData and taskRenderer, so this module is now going to have to depend on taskData and taskRenderer. That also means the code down here in require no longer depends on taskData and taskRenderer because nothing in here actually calls any of those anymore. So, we can remove those. However, it will need to depend on tasks. So, we can add that module as a dependency. And I'll once again use the revealing module pattern here to expose this module's public members. And once again we need to fix the call to our function since it moved by changing the call to render to tasks.render. Finally, we have this last bit of code here, which just initializes some event handlers. I would consider this to be application initialization as a responsibility. So, what I generally do is to define a module that's named app, and usually that is my main application module. You can do this if you want to. If you don't like it, then there's really no need to. I like it just because it separates my main.js file and an app.js file where main really sets up require and app really sets up my application-specific components. As we separate the code into different files, it might make a little bit more sense. But for right now, I'm just going to define app, and we're going to put my event registration in there. And here I'm going to use the regular module pattern and define an init function that's just going to call registerEventHandlers. And since that function is now private within this module, I'm going to prefix it with an underscore. So, now down here I can clean this up a little bit more. And instead of taking in tasks, this module can now take in tasks. And my main require down here can now just call app.init, and we'll pass the app module into here. And that'll take care of the call to registerEventHandlers. With RequireJS, this call to jQuery to pass it a callback function to run when the page is finished loading is not necessary at all, so we could have removed that a long time ago. And then there's just the tasks.render, which we need to call. So, we'll just move that out of here and put it into our app.init. So, now our main require callback is just very simply calling an init function for the rest of our app module. Actually, nothing in here uses jQuery either anymore, so we can take jQuery out. This does use jQuery, so we need to add jQuery here---oops, not there---in this array. And I have a tendency to put third-party libraries first, so jQuery usually ends up as the first thing in my array of dependencies, and then my own modules tend to go last. That's just the way I do it. You can do whatever makes the most sense to you. And I'm sure some of these other modules use jQuery. This one, obviously, has some calls to jQuery in it. So, let's add it there as well. This module also has calls to jQuery. But our taskData did not. And finally, the last thing that I had to do was that, in the app module here, all these event handlers used to be calling functions that have now been moved off to other modules, so we need to add all these functions. And since these functions are being assigned to function handler callbacks, I'm going to give them names that are a bit more representative of what buttons they're tied to. This kind of thing isn't too uncommon when dealing with any kind of dependency injection framework and, really, any language since we're moving things off to different pieces, maybe their modules or classes or whatever. Sometimes, you just have to wrapper up calls that pass through to the next layer. Once again, testing in the browser, we can see that we can still add new tasks, delete the tasks, delete all, and there're no errors in the console. So, our refactoring has gone well. If we were to map out the hierarchy of our modules we've defined, it would look like this. TaskRenderer and taskData get loaded by the tasks module. The tasks module gets loaded by the app module. And the main require function loads the app module.
Simplified CommonJS Wrapper
So far, we've been defining all of our AMD modules by listing a set of dependent modules in an array of strings and then passing those in as the parameters to the callback function. This is really the standard AMD module syntax for setting up module dependencies. However, there's an alternative to this that we should also discuss. You can see here that we're currently only importing two modules, but there can certainly be the case where you're importing six or seven or eight modules and things get a bit cluttered. They tend to run off the right side of the screen pretty quickly. Sometimes, it's a little bit easier to organize this by putting the callback function on a second line and just adding spaces so that the parameters line up. But still if you have a lot of things being imported here, this can still run off the right side of the screen pretty quickly. So, let's look at an alternative that RequireJS makes available that is called the simplified CommonJS wrapper syntax. To use this syntax, instead of specifying this list of imported modules, we just pass a function, and it has to have exactly the parameters require, exports, and module. Module is actually optional, but these need to be here in exactly this order with exactly these names. When that's included, RequireJS will allow you to import other modules using the passed-in require function. So, for importing jQuery, we could say var $ = require jQuery. The string here, again, being the module name, the same as we had in our array earlier. And then we can import our tasks module the same way. Now, also instead of returning an object that becomes our module, we need to do things a little bit differently. We no longer need to return anything here. But we use the exports object that was passed into our function and assign all the things that we want to make public to that exports object. So, in this case, to make my init function available, I would say exports.init =, and set it to the function. It's a bit different syntax, but it follows along with the original CommonJS syntax. And this is actually pretty useful in the case where you have a lot of things being imported into your module because instead of it running off the right side of your screen in a big array list of strings, you can set it up vertically as a set of imports instead. And so here, again, is our list of parameters for the define function now with the simplified CommonJS wrapper syntax added to it.
In this module, we took a look at the define function and how it's used to define our modules using both the standard AMD syntax and the simplified CommonJS syntax. We also looked at the require function and how it's used to initially load all of our modules. We learned that each module is defined by a string name, and we also learned that RequireJS is one of many implementations of an AMD module loader and that AMD stands for Asynchronous Module Definition. We learned about the module and revealing module patterns and how they fit well with the AMD modules. We also refactored our sample application and broke out all of our individual AMD modules. however, this isn't exactly how we want to structure our code during development. So, in the next module, we're going to learn how to load our AMD modules off the server and separate all of our code into separate js files instead of one large file.
Loading Remote Modules
In this module, we will continue to refactor our to-do list application by moving all of our RequireJS AMD modules to separate files. We will then look at some performance implications of this approach and begin to see what we can do to eliminate these issues. Up until this point, we have been working in one large main.js file making it bigger and bigger as we need to add new modules. When developing a real application, you would typically not do this as it would make your code bulky and hard to read, which is one of the original problems we were trying to get away from when using RequireJS. Instead, we want to be able to put each of our AMD modules, which each deal with a single responsibility, into its own file. This lets us organize things nicely in our project during development. Let's begin by looking at our existing sample project.
Define Remote Modules
When we defined each AMD module directly in our main.js file, we gave the module a name. For example, this module is named taskData. When we require or depend on a module, we specify the module's name, as done here in the array of dependencies. When RequireJS needs to load a module, it checks its internal cache of defined modules. If a module with a matching name is already loaded, then it is returned. This is why defining our AMD modules in the same main.js file before the call to require at the end works. However, if a module with a matched name is not found, then RequireJS uses the name as a URL and attempts to load the file. To understand how this works, it is important to know that internally RequireJS keeps track of what is called a base path that is used to load modules from. The module names are considered a URL relative to the base path. By default, the base path is set to the directory that the main.js file is located in. In our sample project, the main.js file is located in the js directory, so all other modules would be loaded relative to this path. Later in this course, in the module on configuring RequireJS, we will see how we can change the location of this base path. As we continue refactoring our sample to-do list application, the next step is to move each of our modules to its own file. Let's start by moving our taskData module to its own file. When building a URL to load a module from, RequireJS will automatically add the .js extension to the requested module name. The file should be placed relative to the base path, which is the /js directory. This means that we want to create a file that is the combination of base path plus the module name plus the .js extension. So, we want to save a new file to js/taskData.js. We then move our code to define the module into this new file. In addition to just moving the code, we also no longer need to specify a module name. This is because RequireJS will use the module name that was requested originally. In other words, because our tasks module is dependent on the module named taskData, RequireJS loaded a file name with the path js/taskData.js and cached the resulting module object with the module name of taskData. The module itself no longer has to tell RequireJS what its name is. This gives us another possible syntax for the define function that we use when defining a module that will be loaded from a remote file. This syntax just has two parameters--the array of dependencies and the callback function that creates the module. We can now do the same thing with each of our other modules.
You can see here that we have created a file for each of our defined AMD modules. However, the project is still lacking some structure. It would be nice if we could further separate these js files into subdirectories. Fortunately, we can do just that. Let's make a new directory to hold our js files related to data saving and loading. We will name the folder data and move the taskData.js module into this directory. Now, we need to fix any references to the taskData module and include the data subdirectory. The tasks module is the only module that depends on taskData, so in this file, instead of depending on taskData, we can now depend on data/taskData. This will cause RequireJS to load this module from js/data/taskData.js, which is the correct path for our taskData module. Now let's do the same with our taskRenderer module. We'll move it into a new directory named renderers and change the reference in the tasks module to include the new subdirectory. This ability to add subfolders is a powerful tool in allowing us to better organize scripts in our project helping all the developers on the project to more easily navigate through the code. However, there is one limitation with subdirectories that you should be aware of. If you decide to put a module in a subdirectory, then it needs to be underneath the base path. If you try to depend on the module and you start its name with .. to try to go up one directory, this will not work. However, we can see a workaround for this issue later in the course in the module on configuring RequireJS.
Performance Implications of Remote Modules
When we now load our sample project in the browser, we can see that everything still appears to be working. However, there's actually a performance problem due to the use of the remotely loaded AMD modules. If we look at the browser dev tools in the Network tab, we can see that RequireJS is making a call to the server for each and every one of our AMD modules. This is going to cause more requests to and more load on our server, and it will also increase the page load time for the user. This also isn't really what we want to do. We have improved our code dramatically at development time making it easier to read, organize, and navigate, but done it at the detriment of performance at the client, which is going to lead to a worse user experience. In the next module, we're going to talk about the r.js optimizer for RequireJS, which will take care of this problem for us and allow us to have a good experience both at development time and at runtime.
In this module, we learned that we can have RequireJS load modules from remote files. We learned that the syntax for the define function changes, no longer specifying a module name when a module is defined in its own file. Subdirectories are used to better organize our files, improving maintainability and organization of our code. However, we also found that when loading modules remotely, a server request is made for each file causing a reduction in performance on page load. In the next module, we will learn how to overcome this issue using the RequireJS optimizer.
In the previous module, we separated our code into multiple files and used subdirectories to organize our code to make it easier to read and maintain. This had great development time benefits because we can have smaller files, avoid spaghetti code, and respect the single responsibility and dependency inversion principles. However, this caused a runtime issue for the end user because each file now has to be loaded from the server. This caused more requests to the server and increased the load time of the client. In this module, we will finally find the right balance between deployment ease and runtime efficiency by using the RequireJS optimizer named r.js to eliminate this problem. The desired outcome of this optimization step is to take all of our individual files, main.js, tasks.js, etc., and combine them into a single file, main-optimized.js. This will reduce the number of server requests the browser has to make. We then want our file to be minified, which will reduce the overall size of the file that needs to be transferred. To accomplish this, we are going to need to set up a build environment for our project.
Build Environment and Running r.js
Debugging and Source Maps
The paths option can be used to override the default paths used for module lookup. To use it, you set the paths option to an object. That object will get a property with a name that matches the path to map from and be set to a string that is the path to map to. There are two ways that this is commonly used. One is to use a simpler name for a module whose file has a complex name. This is exactly what we did in our sample project when loading jQuery. The name of the jQuery file was jquery-2.1.1.min.js. It would be awkward if in each module where we depended on jQuery, we had to reference it by its actual file name jquery-2.1.1.min. To simplify things, we can use a path to tell RequireJS that when we ask for jQuery, it should actually use jquery-2.1.1.min. In this case, we were having the path match an entire module name, but we can also just have it match part of a path. For example, if you want to specify a path in a module name, for example scripts/sample, then it would normally load this module with the URL scripts/sample.js. However, we can specify a different path for scripts that changes it to app/js. In this case, the module script/sample would now be loaded from the URL app/js/sample.js. There's one more really useful case for the paths options. If you remember earlier in this course when we moved our modules into separate files, I mentioned that the files and directories had to be underneath the baseUrl. You could not start a module name with ../ to move up a directory. However, you can start a path with ../, so you could load modules that are up a directory in relation to the baseUrl by specifying in the configuration scripts maps to ../scripts. In this case, the sample module would now be loaded using the URL ../scripts/sample.
The config option gives us a way to pass configuration options down to a module. For example, let's say that we have a URL to a set of web services that we want to have in the global configuration for all modules to use. We could specify the RequireJS config option and set it to an object that contains our URL. Now, when a module needs these config options, it can depend on a special module named module. Module will have a function named config that we can call to get our config object that was specified back in the configuration. This is a nice way to specify your global app configuration in one place and have several modules use it. So, now that I've explained how config works, I'll tell you that I've actually never once used it. When I have configuration that I need to share across modules, I like for it to be in its own file. After all, part of the reason for using RequireJS is to have the ability to better organize our code. Typically, I just make a remote module names config.js to hold my configuration. Then my other modules can just depend on my config module to get the configuration options that they need. This to me looks a lot nicer, but it's up to you.
The waitSeconds option is the number of seconds to wait for a module to finish loading before throwing an error. Remember that module loading is asynchronous. There can certainly be times that we try to load a module remotely from a server, and the server fails to respond. The default is seven seconds. If you run into the situation where a module takes a long time to load, then you may need to adjust this setting. You can also disable the timeout by setting the value to 0.
Deps and Callback
The deps and callback options are used together. So, let's talk about them at the same time. The deps option can be set to an array of module names. These module names will then be loaded as soon as RequireJS starts up. Note that these modules are still loaded asynchronously, and any calls to require that you have in your code can still be called before these modules are finished loading. It is really just there to specify some modules that you want to load right away. The callback option can be set to a function that will be called by RequireJS when all the dependencies in the deps array are done loading. This is another option that I've really never had to use since it would normally just include all the modules I want to load right away in the first call to the require function. These two pieces of code do basically the same thing.
The urlArgs option can be used to provide extra query string options to the URLs that RequireJS uses to load modules. The option can be set to a string, and that string will be appended to the URLs as the query string. You do not need to include the question mark in the setting. The most common use case for this option is for cache busting. By appending a changing string to the query string, the browser is prevented from caching the result. For example, when RequireJS loads module one here, the URL that it uses will be moduleOne?bust= and then a number representing the current time when this page was loaded. Unfortunately, this config option is all or nothing. You cannot add a urlArgs to some module loads but not others.
That was a quick overview of what I think are the most commonly used RequireJS options. Again, there are a few more options, but I've never really seen them used. If you want to explore the full list of possible options, please visit requirejs.org and check the API documentation. In the next module, we're going to discuss RequireJS's plugin framework.
One of the really nice features of RequireJS is that it provides a plugin framework that can be used to extend its default functionality. In this module, we will discuss this plugin framework by taking a look at a couple of commonly used plugins, as well as make a custom plugin of our own. First, you should understand what plugins are. Plugins are used to load individual modules and load them from outside the normal method performed by RequireJS. In other words, instead of using require's built-in mechanism for loading a certain module, the plugin loads the module, then hands it back to require. To specify that a plugin should load the module, you use the syntax plugin name, then an exclamation mark, then the module name as usual anywhere the dependent module is specified. Interestingly, RequireJS plugins are actually AMD modules themselves. Let's start by looking at a simple example. We have a module that has this definition. When RequireJS loads this module, it will see that it first needs to load two other modules. First, it will request moduleOne as it would normally with the URL moduleOne.js. Next, RequireJS will see that the next module uses the superPlugin plugin, as represented in front of the exclamation mark. It will load the module superPlugin using the URL superPlugin.js just as it would any other module. This also means that superPlugin.js is loaded relative to the base URL. However, as we learned in the previous module on configuration options, you could use the path option to tell RequireJS to load superPlugin from somewhere else. Next, it will tell superPlugin to load moduleTwo and return the result. This means that RequireJS will not go and fetch the URL moduleTwo.js. It will be entirely up to superPlugin to deal with loading moduleTwo and handing off the result to RequireJS. If this doesn't make complete sense yet, don't worry. We're going to look at two RequireJS plugins starting with the text plugin.
Unit Testing RequireJS Modules
In this module, we will discuss unit testing of our AMD modules and how RequireJS plays into testing. In general, the nice thing about breaking our code into smaller modules and using a dependency injection framework like Require is that it makes our code more testable. We now have smaller units to test and can inject mocks to test with. However, use of Require actually significantly complicates unit testing. This is due to two issues. First is that module loading is asynchronous, so we need to use a test framework that can deal with asynchronous testing. Second is that Require caches loaded modules. This means that when a module is loaded for one test, the modules will still be cached in Require for the next test. But, don't worry too much about this yet. We'll get to that later. First, let's go over some unit testing basics and set up a test environment.
Testing RequireJS Modules
Squire is an open-source tool for managing RequireJS's contexts and injecting mocks into the context for us. It will replace all the code in the beforeEach and afterEach that we just added. You can download Squire from GitHub at github.com/iammerrick/Squire.js. You really only need to download one file, and that's in the source directory and is called Squire.js. I'm going to save this file into the lib directory of our task list project, but you could save it to wherever it makes sense to you and fits the layout of your project. Squire itself is actually an AMD module itself, so we will load it into our test spec using Require. Wherever you save Squire.js to, it is unlikely to be in the base URL directory, so let's add a path to the require configuration so that it knows where to load Squire from. In our case, that would be ../../lib/Squire. Now in the beforeEach that runs before each of our tests, we want to make a new instance of Squire. Since Squire is an AMD module, we need to use RequireJS to load it by making a call to require. Remember that this loading is asynchronous. So, to handle that, we add the parameter done to the functions passed to the beforeEach and call done after Squire has been loaded and we create an instance. We will save this instance into a variable named injector. The Squire constructor will create a new RequireJS context for us and also copy all the configuration options from the default context. This replaces all the setup code we had to do ourselves previously with just this one line. We will also need to tell Squire to clean up the RequireJS context when the test is complete. So, in the afterEach function, we call injector.remove. This will delete the RequireJS context that was created for the test. Previously, to set up a mock version of the taskRenderer module, we had to make a call to define. When using Squire, it provides a function named mock that we can use instead. Injector.mock takes two arguments. The first is the string name of the module. The second is the mock object instance to return when this module is required. This code will define the renderers/taskRenderer module in the RequireJS context that was created by Squire. Finally, instead of using the require function from the global scope to load our modules for the test, we instead use injector.require. This gives us the special require function that is bound to our test context that contains the mocked objects. Now, we can run the SpecRunner.html file again in the browser and verify that our test still passes. Using Squire is a lot easier than setting up and managing RequireJS contexts ourselves. There're some other nice features provided by Squire, so I encourage you to check out the documentation on GitHub.
In this module, we focused on unit testing our application. We had a brief introduction to the Jasmine test framework and discovered that Jasmine 2 has support for asynchronous testing. We also learned that internally RequireJS can manage several contexts. Each context maintains its own configuration settings and module cache. Contexts are created using the special context setting when calling require.config. AMD modules are a nice way to isolate code into single testable responsibilities, but RequireJS makes testing a little more difficult. This is due to asynchronous module loading, which we overcame with Jasmine 2's asynch support using the done function, and also due to RequireJS caching loaded modules, which we overcame with RequireJS contexts and Squire. Setting up a proper test environment with Jasmine, Require, and Squire can be a bit of a hassle, but in the end, it's well worth it to be able to test your code. I encourage you to go through the exercise of setting up a test environment at least once and save it off somewhere so that you can replicate it later. This would save you the time of going through all these steps each time you make a new project.
Released13 Jan 2015