RequireJS jump start

It’s surprising how many senior JavaScript developers don’t know anything about JS Modular Programming. But come to think about it, if my code is working, why do I bother to learn all these AMD, CommonJS, RequireJS stuff? But once you understand it, JS modular programming really makes a big difference. Your code becomes much cleaner and it will require less effort to maintain.

There are many documentations regarding AMD and RequireJS out there. Here I just wanted to provide a quick start guide on RequireJS so people can start using it quickly and find out the rest 90% of the information later.

First let’s briefly talk about the problem we are trying to solve – say we have some code in a .js file that is referenced by a script tag in a HTML page and it defines a few variables and functions. All these stuff will be placed into the global variable set, which is very likely to cause naming conflicts when the project size gets bigger.

That’s why people invented Module Pattern to help encapsulate JS code and reduce the need for creation of global variables. An simple example:

(function (){
   // Now we have a private scope
   var localVar ={};
   function localFunc () {}

   // 'output' a function into global
   this.globalFunc = function(){
       // both localVar and localFunc are available.
   };
}());

In the example, we created a anonymous function block (i.e. anonymous closure) where we can create variables available locally to a global function, globalFunc. However this is still not optimal because it defines a global variable within this block on which the code references it has no control.

Besides this global pollution issue, we also have a dependency issue. Using script tags to load scripts, we have to make sure dependencies are loaded in the correct order. For example, Backbone cannot be loaded before jQuery.

These are the 2 major issues I want to resolve using RequireJS. Now let’s start using RequireJS!

First download Require.js

Create a config file for RequrieJS for easy configuration

/src/requiresjs/config.js

require.config({
  // Set up base path on which the rest of the paths are based
  baseUrl: "../..",
  paths: {
    // This defines where your dependency is.
    // Note that file extension is not needed.
    jquery: "scripts/jquery/jquery",
    // backbone and underscore won't work just like this, but we will get to it later.
    backbone: "scripts/backbone/backbone",
    underscore: "scripts/underscore/underscore"
  }
  // This defines the startup script of your application.
  deps: ["src/main"]
});

Then in your main html page, add just one script tag. Just specify the path of RequireJS and set the path of the config file in the data-main attribute. What this does is to allow RequireJS to find the config file and start your main.js script specified in the config file.

/src/pages/index.html

<script type="text/javascript" src="/scripts/requirejs/require.js" 
data-main="/src/requirejs/config">
</script>

In your main.js file, define a module.

/src/main.js

define(["jquery", "underscore", "backbone"], function($, _, Backbone) {
  // The first array argument includes the path variables defined in the config file.
  // The second argument is a definition function that accepts modules as arguments.
  // It will be called after all the modules in the first argument are loaded.

  // Now you have variable $ for jQuery, _ for underscore, and Backbone for backbone.js
});

The example shows that RequireJS allows us to load a module in js code at the time we need it, and we have full control on what variable name it should be assigned to. Moreover, it automatically loads all the dependencies of the module before it is loaded.

Back to our config file, the setup for backbone and underscore is not complete, because they are non-AMD scripts. Simply put, they didn’t follow AMD standard to define themselves. For these modules, we need to add in the config file a shim configuration.

/src/requiresjs/config.js

require.config({
  baseUrl: "../..",
  paths: {
    jquery: "scripts/jquery/jquery",
    backbone: "scripts/backbone/backbone"
  },
  shim: {
    backbone: {
      exports: "Backbone",
      // Specify the dependencies of backbone
      deps: ["underscore", "jquery"]
    },
    'underscore': {
       exports: '_'
    }
  }
  deps: ["src/main"]
});

Now the configuration and the example are complete.

Note that there is another way to define your module. I prefer this approach just because it looks more straightforward. It is called CommonJS style definition.

/src/main.js

// just pass in a require argument, it will work.
define(function(require) {
  // find modules' paths using the variables defined in the config file
  // and put it in a local variable.
  var $ = require("jquery");
  var _ = require("underscore");
  var Backbone = require("backbone");
});

There is much more about RequireJS and AMD in general that one can dig into. I recommend to read more from RequireJS’s site, but hopefully this gives you a general idea of modular programming and something to start with.

Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s