Managing your JavaScript Dependencies with RequireJS

RequireJS is a module loader for JavaScript. RequiresJS is based on the AMD (Asyncronous Module Definition) API specification, which provides a mechanism for defining and consuming modules. When you require or use a module, it will be retrieved asynchronously and cached at the time you need it, that improves performance since you don’t need to load all your dependencies (JavaScript files) at the same time.

Having performance improvements in order to provide a better user experience is great, but there’s a another recurrent issue in web projects, how do we handle our dependencies in order to avoid conflicts between them?. As we know, web browsers natively support the “script” tag which allows us to include external javascript files in html pages.

<script src="//code.jquery.com/jquery-1.11.3.min.js"></script>

But what happens if we include two different versions of JQuery:

<script src="//code.jquery.com/jquery-1.11.3.min.js"></script>
 <script src="//code.jquery.com/jquery-1.2.6.min.js"></script>

What you see above is not unusual, two developers might be working on different features for a page and both need an specific version of a library (Maybe a plugin depends on an specific version of JQuery). In that case the feature of one of the developers will be broken since the resolved version will be “1.2.6”, even both versions are included. With RequireJS modules, developers can isolate their logic in order to avoid this type of conflicts.

Example

Project Structure

  • requirejs-example
    • index.html
    • js
      • jquery-1.11.3.min.js
      • main.js
      • require.min.js
      • utils/test-module.js

index.html

It includes a single script ‘require.js’ with a data-main attribute pointing to the requirejs configuration script ‘js/main’. RequireJS suggests to keep all inline scripts out the HTML, in order to take advantage of the optimization tool.

<!DOCTYPE html>
<html>
    <head>
        <title>RequireJS Example</title>
        <!-- data-main attribute tells require.js to load
             js/main.js after require.js loads. -->
        <script data-main="js/main" src="js/require.min.js"></script>
    </head>
    <body>
        <h1>RequireJS Example</h1>
    </body>
</html>

js/main.js

We’ll use this script to configure our dependencies, including third party libraries such JQuery.

//dependencies configuration
requirejs.config({
    //Sets the base url for resolving our modules
    baseUrl: '../js',
    //Define some paths  or alias to third party modules
    paths: {
        //We can use JQuery as module just defining a path, given that it is implemented as a module using AMD spec
        'jquery': 'jquery-1.11.3.min'
    }
});


js/utils/test-module.js

//First parameter is an array of dependencies ['jquery'].
// Second parameter is a function in which we'll define our module
define(['jquery'], function(jquery113){
    // We have to return the module implementation (function, constructor function, plain object etc.)
    return {
        doTest: function() {
            //Our logic is simple, just an alert with the jquery module version.
            alert("Jquery Version is" + jquery113.fn.jquery);
        }
    };
});

test-module.js defines a simple module ( a plain JS object), with a method using jQuery (the required dependency). Note that ‘jquery’ is the alias/path we’ve defined in our main.js configuration file for jQuery.

Now we can use our module:

main.js

require(['utils/test-module'], function(testModule){

    testModule.doTest();
});

The module and all the dependencies are loaded asynchrounously.

Conclusion

RequireJS provides a way to write modular JavaScript in the web browser, improving performance, providing better cohesion and reducing coupling. It also resolves a recurrent issue in web development the “dependency hell” when using “script” tag.

Code available on github https://github.com/ottogiron/requirejs-example


comments powered by Disqus