Skip to content

Upgrading to RequireJS 2.1

jrburke edited this page Oct 15, 2012 · 2 revisions

Why

RequireJS 2.0 was a major improvement over 1.0 as far as execution rules and the ability to integrate non-modular code into a modular project. RequireJS 2.1 refines both the execution rules and integration of non-modular code. 2.1 is very similar to 2.0, but it has enough noticeable changes that it did not make sense as a 2.0.x point release.

The bigger execution rule changes are better cycle breaking, and improved support for cycles in transpiler plugin resources. require([], function (){}) is async now in a couple cases where it was not before. The shim config has a change related to function-based exports values.

Support for the AMD APIs has not changed. There is a small change to the loader plugin's load.fromText API, but RequireJS 2.1 can still run loader plugins written for RequireJS 2.0 without modification.

If you are upgrading from RequireJS 1.0, be sure to read Upgrading to RequireJS 2.0 first. 2.1 builds on the 2.0 work.

If you are using the almond AMD API shim, be sure to use almond 0.2.0+ with RequireJS 2.1.0+, since those versions of almond will have changes to match the RequireJS 2.1.0+ changes.

As always, it is best to match the versions of require.js and the r.js optimizer.

Breaking Changes

These changes are the ones that you will most likely notice. The shim feature was a new one in 2.0, and one of its less-used forms needed a change. Also, the existing, implied require([]) async behavior is more strongly enforced, and that enforcement may break code that did not properly take async into account.

shim exports and init

If you were using the shim config and specified a function value for the "exports" property, that will no longer work. Instead, "exports" is always a string in 2.1, and there is a new property "init" that can be a function. Set up an "init" if you were previously using an "exports" function.

This change was done so that enforceDefine could always be used with a shim config, even if it needed to do some functional work. With exports as a function, it was impossible to use enforceDefine, and therefore get load errors in IE.

So, in 2.0 if you used something like this:

requirejs.config({
    shim: {
        'foo': {
            deps: ['bar'],
            //A function can be used to generate the exported value.
            //"this" for the function will be the global object.
            //The dependencies will be passed in as function arguments.
            exports: function (bar) {
                //Using a function allows you to call noConflict for libraries
                //that support it. However, be aware that plugins for those
                //libraries may still want a global.
                return this.Foo.noConflict();
            }
        }
    }
});

for 2.1 and later, write it like this:

requirejs.config({
    shim: {
        'foo': {
            deps: ['bar'],
            exports: 'Foo',
            init: function (bar) {
                //Using a function allows you to call noConflict for
                //libraries that support it, and do other cleanup.
                //However, plugins for those libraries may still want
                //a global. "this" for the function will be the global
                //object. The dependencies will be passed in as
                //function arguments. If this function returns a value,
                //then that value is used as the module export value
                //instead of the object found via the 'exports' string.
                return this.Foo.noConflict();
            }
        }
    }
});

Enforcing async require([])

In some cases require([], function (){}) would start dependency tracing immediately instead of waiting for a future turn of the event loop. This caused some subtle bugs, particularly after a build, where the required modules were in the file, but after the require([]) call that referenced them.

This has been fixed now, and that fix carries over to using requirejs in Node to load modules. So, using requirejs in node always runs the dependency tracing and function callback in a future event loop turn. Example:

var requirejs = require('requirejs');
var f;
requirejs(['foo'], function (foo) {
   f = foo;
});
//In 2.0 this would print a value. In 2.1+, f is still undefined at this point.
console.log(f);

This is most important if you are running unit tests in Node that assumed the synchronous 2.0 behavior above. You now need to adjust your code. If you are using Mocha, see this requirejs list thread.

In 2.1.1+, you can ask requirejs to synchronously load code in Node, but that must be done via requirejs('stringValue') calls:

var requirejs = require('requirejs');
var foo = requirejs('foo');
//This will print a value for foo.
console.log(foo);

Other Changes

  • Cycle breaking is greatly simplified and works better than before. If you were seeing weird "undefined" errors sometimes when the text plugin was used, that should be fixed now.
  • Transpiler loader plugins now can support similar cycles to the ones supported by requirejs in plain JS modules.
  • Loader plugins can implement an onLayerEnd method to get notified during optimizer builds when writing to a build layer that uses the loader plugin ends.
  • The loader plugin's load.fromText() API is a bit simpler now, and it can only be called once per transpiler resource ID. This part of the cycle support changes. See the load.fromText API section for more details. Code written for the old API will still work in 2.1 as long as the loader plugin was only calling it once per resource ID.

The full set of changes/bug fixes are in the issues marked for 2.1:

Report issues

The requirejs list can be used for discussions.