As web applications grow both in size and function, the dependencies that your application requires grow exponentially. In such times, you need a scalable way of organizing your JavaScript libraries. One way is called modular programming, which helps you to divide your application’s code base into multiple modules and connect them with bridges.
What Is RequireJS?
RequireJS is a JavaScript framework that works atop the Asynchronous Module Loader (AMD). The AMD loads the JavaScript files and modules in an asynchronous fashion. It can be used in browsers as well as in a Node.js environment.
The main advantage of using frameworks like RequireJS is that it helps you to modularize the code base into multiple small pieces while still allowing easy maintenance. This in turn lets you require only the library you need.
Let’s assume a single-page application is built with various components like a login, a dashboard, and a settings panel. Every one of these pages has its own dependencies. The dashboard could be dependent on libraries like D3 and ChartJS, while the settings panel could be dependent on Fileuploader and DropZone. The traditional way is to load all the JavaScript files at first run. Clearly this is not scalable.
With RequireJS, we can define which library should be loaded and when and where, exponentially increasing the performance and network speed of the application.
Getting Started
In order to use RequireJS, you need to import this framework before all other JavaScript libraries. Since it’s written in plain old JavaScript, it isn’t dependent on anything else.
You can import it via a CDN:
In the above line, the data attribute data-main tells the browser to load the scripts/main.js file after loading the require.js file.
It’s also a good idea to organize all your JavaScript files and dependencies under scripts/helpers or in the scripts/lib folder.
scripts/main.js is the file that has the RequireJS functions that handle compilation pipelines to order the execution flow.
Basic Implementation
Now let’s create a simple scenario where you’ll have a set of functions in a file called scripts/helpers/utils.js and a dependent logic inside scripts/main.js. I’ll create a simple console statement as an example.
Contents of scripts/helpers/utils.js
console.log('You're in utils');
Content of scripts/main.js
requirejs(["helper/util"], function(util) { console.log('Inside Main') });
When you execute index.html, you’ll notice the order of execution. First, the utils statement gets executed, then everything else, and only after that is the code inside the callback function executed.
Syntax Explanation
The requirejs function takes two parameters. First is the array of dependencies to be loaded, each representing the absolute or relative path where they are located. The second is the callback function that will take the variables of the dependencies as parameters and will hold our JavaScript statements and functions to be executed after loading the dependencies.
Let’s try the same with a real-world scenario where we’ll be calling a set of statements after loading the jQuery library.
Change the contents of main.js to:
requirejs(["helper/jquery"], function(util) { $('body').css({backgroundColor: "red"}) });
This will make sure that the statement is called only after the jQuery library is loaded to the browser and ready to use. Thus, the $ variable is available for all the following statements. This makes sure that the dependencies are loaded in a proper sequence.
Loading Multiple Dependencies
When you have more than two dependencies (which is a very common scenario in any modern web application), you can reference all the dependencies in an array and the callback will be called once all the libraries are loaded.
requirejs(["helper/jquery", 'helper/moment'], function(jquery, moment) { console.log(moment().format('LLL')); });
In the above-mentioned example, we’re loading two libraries (jQuery & MomentJS). Here, both are located under the helpers folder. As you may notice, the callback function takes two arguments, which denotes the libraries’ variables respectively.
You can have as many libraries as desired in the array, but it’s suggested to keep them at a minimum for better performance.
Nested Dependencies
Sometimes you have to load libraries that are dependent on other libraries like jQuery. This happens mostly when you use plugins for jQuery. In such a case, you can load the files in a nested pattern.
In our example, let’s use the autosize plugin, which makes the text area auto-resize when the content’s height grows. This is a jQuery-based plugin, thus it should be loaded after jQuery is initialized. In order to do this, we’ll be nesting the dependencies like so:
requirejs(["helper/jquery"], function(util) { requirejs(["helper/jquery.autosize.min"], function(util) { $('textarea').autosize() }); });
Configuration
RequireJS comes with a lot of configuration options that help you to manage dependency loading in an efficient way.
To create a custom configuration, you’ll be using the requirejs.config() function and will be passing the config object as a parameter.
Here are some basic and most commonly used configuration options:
- baseUrl: This will be used as a suffix to the URL of the loaded dependencies. Used when you have all the library files in a common folder. (In our example, it will be the helpersfolder.)
- paths: This option provides a mapping for libraries that are not present under the base URL. The keys of this object define the library names, and the value defines the path where they are located. It’s considered that the path is relative to the baseUrl unless it starts with / or https://.
- bundles: This is helpful in scenarios where a single module has a lot of submodules. If you need to use a submodule, then you need to know in which module it is located. With the bundles option, you can define it. Here the key defines the parent module, and the child array has the list of all the modules that will be found by loading the parent module.
requirejs.config({ baseUrl: 'scripts/helper', paths: { 'jquery': 'lib/jquery', 'moment': 'lib/moment', 'lodash': 'lib/lodash', }, bundles: { 'lodash': ['reverse'], } }); requirejs(["jquery", 'moment', 'reverse'], function(jquery, moment, reverse) { console.log(moment().format('LLL')); console.log([1,2,3].reverse()); requirejs(["lib/jquery.autosize.min"], function(util) { $('textarea').autosize(); }); });
Here, we’ve mentioned the baseUrl as scripts/helper as we’re executing the script in index.html. We’ve also defined the path of each library. We have imported lodash as well, and in the bundles option, we have mentioned that the reverse module is loaded from the lodash module. Therefore, we can use it without loading lodash.