Wednesday, February 23, 2011

RequireJS

Dependency management and modularization are hot topics in JavaScript right now. In classical OO languages like C#, we often take dependency management for granted, as there is an abundance of tools available for this purpose. JavaScript is a powerful, flexible language but still sorely lacking in the tools sector. Traditionally, dependencies have been manually configured by specifying script tags in transitive dependency order. This approach, while effective, becomes tedious and difficult to maintain as the volume of scripts grows. In today’s large-scale JavaScript applications, streamlined and efficient dependency management is becoming more and more crucial.

Related to dependency management is the modular approach to designing JavaScript applications, separating functionalities into physically separate files roughly following OOP’s single responsibility principle: http://en.wikipedia.org/wiki/Single_responsibility_principle. Clearly, this approach lends itself to a large number of files in all but the simplest applications, and dependency management/module load order then becomes critical.

Fortunately, the JavaScript community has taken note, and tools are emerging to bridge these gaps. Among those I researched as candidates were:

YUI Loader from Yahoo: http://developer.yahoo.com/yui/yuiloader/
StealJS, part of JavaScriptMVC: http://www.javascriptmvc.com/docs/stealjs.html#&who=stealjs
Lab.js: http://labjs.com/
RequireJS: http://requirejs.org

As of this writing, I’m leaning towards RequireJS for a couple of significant reasons:

1) It’s a port of the proposed CommonJS specification for modular JavaScript. CommonJS (http://www.commonjs.org/) came out of the server-side JavaScript movement, and defines an API by which functionality can be packaged into interoperable JavaScript modules. CommonJS is gaining real traction, and has a strong change at widespread acceptance.

2) The syntax required to define RequireJS modules imposes minimal impact on how I write modules. It essentially entails just wrapping my modules in the API’s define() function:

//my/shirt.js now has some dependencies, a cart and inventory
//module in the same directory as shirt.js
define([“./cart”, “./inventory”], function(cart, inventory) {
        //return an object to define the “my/shirt” module.
        return {
            color: “blue”,
            size: “large”,
            addToCart: function() {
                inventory.decrement(this);
                cart.add(this);
            }
        }
    }
);

So far, I’m really pleased with the implementation. Modules explicitly describe their own dependencies, and the API ensures a file is only loaded once. The API uses asynchronous script loading via insertion of script tags in the document head, allowing browser-legal cross-domain requests. You can specify multiple script paths, and define multiple object contexts so that multiple instances of a module can be defined in a single page.

This is a very powerful, well thought-out API that looks like a good candidate for effective and efficient module loading and dependency management. I’ll post more as I dig deeper…


No comments:

Post a Comment