RequireJS

javascript-amd

http://requirejs.org/
http://msdn.microsoft.com/en-ca/scriptjunkie/ff943568.aspx - LABjs & RequireJS: Loading JavaScript Resources the Fun Way - done reading
http://requirejs.org/docs/why.html
http://requirejs.org/docs/whyamd.html
http://requirejs.org/docs/commonjs.html
http://net.tutsplus.com/tutorials/html-css-techniques/optimize-your-css-with-require-js/ - done reading
http://blog.falafel.com/building-single-page-applications-with-canjs-and-requirejs/ - done reading
http://requirejs.org/docs/optimization.html

What is the purpose of RequireJS?

It is a script loader for JavaScript.

Can we use RequireJS to load JavaScript files that does not comply to the RequireJS module format?

Yes.

How can we use RequireJS?

<html>
<head>
<script data-main="scripts/main" src="scripts/require.js"></script>
<script type="text/javascript">
//The scripts will be fetched in parallel
//and asynchronously, and they could be
//executed in a different order than specified below.
requirejs([
        "script1.js",
        "script2-a.js",
        "script2-b.js",
        "script3.js"
    ],
    function(){
        //This function is called once all the scripts
        //have loaded and executed.
        initScript1();
        initScript2();
        initScript3();
    }
);
</script>
</head>
<body>
...
</body>
</html>
requirejs.config({
    baseUrl: 'js/lib',
    paths: {
        // the left side is the module ID,
        // the right side is the path to
        // the jQuery file, relative to baseUrl.
        // Also, the path should NOT include
        // the '.js' file extension. This example
        // is using jQuery 1.9.0 located at
        // js/lib/jquery-1.9.0.js, relative to
        // the HTML page.
        jquery: 'jquery-1.9.0'
    }
});
require(["some/script.js"], function() {
    //This function is called after some/script.js has loaded.
});

You could also place the script tag end of the <body> section if you do not want the loading of the require.js script to block rendering. For browsers that support it, you could also add an async attribute to the script tag.

Inside of main.js, you can use requirejs() to load any other scripts you need to run. This ensures a single entry point, since the data-main script you specify is loaded asynchronously.

requirejs(["helper/util"], function(util) {
    //This function is called when scripts/helper/util.js is loaded.
    //If util.js calls define(), then this function is not fired until
    //util's dependencies have loaded, and the util argument will hold
    //the module value for "helper/util".
});

The other (recommended) solution is to just name the file 'jquery.js' and place it in the baseUrl directory. Then the above paths entry is not needed. You can avoid lots of configuration lines by placing the files according to the default ID-to-path convention of baseUrl + moduleID + '.js'. The examples below show how to set baseUrl to be the directory for third-party, library code, and use one extra paths config for your app code. So to reiterate, you will likely get an error if you refer to jQuery with another module name, like 'lib/jquery'. This example will not work:

// THIS WILL NOT WORK
define(['lib/jquery'], function ($) {...});

It will not work, since jQuery registers itself with the name of 'jquery' and not 'lib/jquery'. In general, explicitly naming modules in the define() call are discouraged, but jQuery has some special constraints.

Whereas LABjs supports enforcing execution order by inserting .wait()’s into the chain, RequireJS does not natively support executing asynchronously loaded scripts in a particular order. However, thanks to the efforts of Alex Sexton, as of RequireJS 0.12, the order plugin offers basic support for this feature.

RequireJS has a plugin architecture that allows you to add new types of loading or dependency types. There are plugins for i18n string bundles, fetching text files (which can be inlined by the optimization tool), treating a JSONP service as a dependency, and forcing execution order of the dependencies.

The order plugin uses the same underlying technique as in LABjs: the type=”script/cache” fake MIME-type trick to load scripts in parallel but be able to manually execute them when appropriate.

It should be noted this trick is only necessary/effective in IE, Chrome, and Safari. For Firefox/Gecko and Opera, the trick doesn’t work, but thankfully isn’t needed because those browsers will automatically ensure the proper execution order even with dynamically loaded script elements.

How can we use the order plugin?

<html>
<head>
<script src="require.js"></script>
<script type="text/javascript">
//Since the order plugin was used, the
//scripts will be fetched in parallel
//and asynchronously, but executed
//in the order specified below.
require([
        "order!script1.js",
        "order!script2-a.js",
        "order!script2-b.js",
        "order!script3.js"
    ],
    function(){
        //This function is called once all the scripts
        //have loaded and executed.
        initScript1();
        initScript2();
        initScript3();
    }
);
</script>
</head>
<body>
...
</body>
</html>

This code looks similar to the code above without the order plugin. However, notice that the scripts are prefixed with 'order!'.

What is the purpose of the require.def function?

RequireJS encourages the use of modules. Modules in RequireJS are defined using the require.def() function call, where the name of the module, its dependencies and a function callback are given. The function callback is called once the dependencies are ready, and it is used to define the module. If the dependency also uses require.def() to define itself, then you can get a direct handle on the dependency in the function callback.

require.def("lamp", ["Light"], function (Light) {
    //This function is called once the "Light" dependency
    //module is loaded and defined. The function
    //argument, Light, will be a reference to the Light module.
    //You can also use require("Light") inside this
    //function to get the Light module.

    //The value returned from this function
    //will be the definition of the "lamp" module.
    return {
        light: new Light(),
        on: function () {
            this.light.on();
        },
        off: function () {
            this.light.off();
        }
    }
});

Notice the name of the module and its dependencies do not include a “.js” suffix. You could use .js URLs directly to load scripts that do not participate in defining a well-scoped module. However, with items that define modules via require.def(), you should always use the same name for the dependency as the dependency uses in its require.def() call’s first argument. RequireJS will translate the name “lamp” to “a/path/to/lamp.js” to load the script.

RequireJS modules can avoid exporting values to the global namespace, and since all dependencies are referenced via string values, it makes it possible to have multiple versions of a module in a page.

The format of RequireJS modules is similar to CommonJS modules, and RequireJS has a command line script you can use to convert CommonJS modules to the RequireJS syntax, allowing you to reuse CommonJS modules in your browser-based development. If you have a server process that can bundle CommonJS modules in the CommonJS Transport D format, like Kris Zyp’s Transporter, RequireJS allows you to use that code directly via the RequireJS Transport D adapter.

Using modules, our main example could have its scripts written like so:

//Contents of script1.js:
require.def("script1", function () {
    //Do the initScript1() work in here
    //var script1 = ...;
    return script1;
});

//Contents of script2-a.js:
require.def("script2-a", ["script1"], function (script1) {
    //Do initScript2() work that only concerns script2-a here. Use
    //script1 as needed.
    return {};
});

//Contents of script2-b.js:
require.def("script2-b", ["script1", "script2-a"],
    function (script1, script2a) {
        //Do initScript2() work that only concerns script2-b here. Use
        //script1 and script2a as needed.
        return {};
    }
);

//Contents of script3.js:
require.def("script3", ["script1", "script2-a", "script2-b"],
    function (script1, script2a, script2b) {
        //Do initScript3() here. Use script1, script2a and
        //script2b as needed.
        return {};
    }
);

How can we use the optimization tool?

RequireJS also comes with an optimization tool that allows you to combine and group your scripts into a smaller set of minified scripts that load quickly.

The RequireJS optimization tool makes it easy to combine scripts together, and you can configure RequireJS to priority-load those combined script layers up front, therefore giving you the best performance while allowing your JavaScript source to stay small and modular. Best of all, since the optimization tool is a command-line tool, you do not need special server side gear for it to work, and its output is deployable to static file systems and CDNs.

If the optimization tool was used to combine the require.def() versions of script2-a.js and script2-b.js into a layer called script2layer.js, then Listing 10 shows how you could code the HTML to get the fastest performing code using three script requests.

<html>
<head>
<script src="require.js"></script>
<script type="text/javascript">
//You can pass a configuration object as the first
//argument to require. "priority" tells the loader
//to download that set of scripts first, in parallel
//and asynchronously before tracing other dependencies.
require({
        priority: ["script1", "script2layer", "script3"]
    },
    ["script1", "script2-a", "script2-b", "script3"],
    function(){
        //Do any page-level work you wanted
        //with all of those modules here
    })
;
</script>
</head>
<body>
...
</body>
</html>

By using the Optimization tool with the priority configuration option, you can then play with the right number of scripts to download to give you the best performance. Since require() can be called at any time during the page lifecycle, you can delay loading some scripts until a later user action.

What are the differences between LABjs and RequireJS?

LABjs works best in pages with today’s scripts that need to be efficiently loaded in a particular order, and the dependencies can all be tracked at the top level. LABjs is so performant because it loads all scripts in parallel, regardless of location, and executes them in proper dependency order as needed. To assist with dependency management, a server-side LABjs component is forthcoming. LABjs is ~2.2k when minified and gzip’d.

RequireJS allows you to efficiently load today’s scripts but encourages a modular approach with CommonJS code sharing and nested dependency resolution. The built in optimization tool can reduce the number of scripts that need to be loaded and to minifies the scripts for even better performance. RequireJS allows you reuse your modules in more than one JS environment. RequireJS can be built in a few ways: from the very basic 2.9k version that does not include plugin capability, multiversion and page ready support, to the 6k deluxe version that includes plugin capability, multiversion and page ready support, and the text, i18n, JSONP and order plugins. All sizes are minified and gzip’d.

How can we use RequireJS with jQuery's plugins?

This example shows how to use the shim config to specify dependencies for jQuery plugins that do not call define(). This example is useful if you have an existing jQuery project you want to convert and do not want to modify the sources of the jQuery plugins to call define().

jQuery itself registers as an AMD module and can easily be loaded. Most plugins, however, do not register as AMD modules, and therefore, require.js doesn't know that the plugins need jQuery to be loaded.

requirejs.config({
    "baseUrl": "js/lib",
    "paths": {
      "app": "../app"
    },
    "shim": {
        "jquery.alpha": ["jquery"],
        "jquery.beta": ["jquery"]
    }
});

// Load the main app module to start the app
requirejs(["app/main"]);

App/main.js is where the app logic is:

define(["jquery", "jquery.alpha", "jquery.beta"], function($) {
    //the jquery.alpha.js and jquery.beta.js plugins have been loaded.
    $(function() {
        $('body').alpha().beta();
    });
});

If all of your modules (including any third party jQuery plugins or library code that depend on jQuery) are AMD compatible and you want to avoid having $ or jQuery in the global namespace when they call requirejs(['jquery']), you can use the map config to map the use of jQuery to a module that calls noConflict and returns that value of jQuery for the 'jquery' module ID.

You can use this example with the CDN example above — the shim example will not work since shimmed libraries need a global jQuery.

requirejs.config({
    // Add this map config in addition to any baseUrl or
    // paths config you may already have in the project.
    map: {
      // '*' means all modules will get 'jquery-private'
      // for their 'jquery' dependency.
      '*': { 'jquery': 'jquery-private' },

      // 'jquery-private' wants the real jQuery module
      // though. If this line was not here, there would
      // be an unresolvable cyclic dependency.
      'jquery-private': { 'jquery': 'jquery' }
    }
});

// and the 'jquery-private' module, in the
// jquery-private.js file:
define(['jquery'], function (jq) {
    return jq.noConflict( true );
});

This means that any module which uses jQuery will need to use the AMD return value rather than depending on the global $:

requirejs(['jquery'], function( $ ) {
    console.log( $ ) // OK
});

requirejs(['jquery'], function( jq ) {
    console.log( jq ) // OK
});

requirejs(['jquery'], function( ) {
    console.log( $ ) // UNDEFINED!
});

Should we use RequireJS for server-side NodeJS?

Yes. Node has a loader, but that loader uses the CommonJS module format, which is non-optimal for the browser, and there are some trade-offs made in the CommonJS module format. By using RequireJS on the server, you can use one format for all your modules, whether they are running server side or in the browser. That way you can preserve the speed benefits and easy debugging you get with RequireJS in the browser, and not have to worry about extra translation costs for moving between two formats.

Can we use Node modules already written in the CommonJS module format?

npm install requirejs

Yes. The Node adapter for RequireJS, called r.js, will use Node's implementation of require and Node's search paths if the module is not found with the configuration used by RequireJS, so you can continue to use your existing Node-based modules without having to do changes to them.

RequireJS will use its Configuration Options first to find modules. If RequireJS cannot find the module with its configuration, it is assumed to be a module that uses Node's type of modules and configuration. So, only configure module locations with RequireJS if they use the RequireJS API. For modules that expect Node's APIs and configuration/paths, just install them with a Node package manager, like npm, and do not configure their locations with RequireJS.

Best practice: Use npm to install Node-only packages/modules into the projects node_modules directory, but do not configure RequireJS to look inside the node_modules directory. Also avoid using relative module IDs to reference modules that are Node-only modules. So, do not do something like require("./node_modules/foo/foo").

Other notes:

  1. RequireJS in Node can only load modules that are on the local disk — fetching modules across http, for instance, is not supported at this time.
  2. RequireJS config options like map, packages, paths are only applied if RequireJS loads the module. If RequireJS needs to ask the node module system, the original ID is passed to Node. If you need a node module to work with a map config, inline define() calls work, as shown in this email list thread.
var requirejs = require('requirejs');

requirejs.config({
    //Pass the top-level main.js/index.js require
    //function to requirejs so that node modules
    //are loaded relative to the top-level JS file.
    nodeRequire: require
});

requirejs(['foo', 'bar'],
function   (foo,   bar) {
    //foo and bar are loaded according to requirejs
    //config, but if not found, then node's require
    //is used to load the module.
});
Unless otherwise stated, the content of this page is licensed under Creative Commons Attribution-ShareAlike 3.0 License