Dojo (core) and Dijit 1.6 have been refactored to follow the proposed CommonJS AMD API.

Module Compatibility

Dojo modules are now completely compatible with:

Flexibility, Performance, and Stack Traces

This refactoring gives Dojo excellent flexibility going forward, to support both legacy synchronous loading mechanisms, as well as new asynchronous script-tag based loading that provides significant performance boosts and debugging improvement (including real stack traces!).

Anonymous Modules

The AMD module format also has a significant advantage in that it allows for anonymous modules, which removes the tight coupling between code and module identity. With anonymous modules, identity is DRY, avoiding the duplication of information in filename and in code. This makes code much more portable, it can be moved to different directories or packages without altering the code itself. AMD also uses slash-delimited module identifiers rather than dot-delimited, which enables more direct mapping to URLs and allows for relative references to other modules, giving us portability of directory structures as well (a directory with a set of modules can be moved or renamed, and the relative ids would still be valid).

Backwards-Compatible

First, don’t worry, your current code based on dojo.provide/dojo.require will still work. By default, Dojo uses a backwards-compatible synchronous loader. You can continue to write:

// synchronously load dijit.Tree
dojo.require("dijit.Tree"); 
// dijit.Tree is now available
new dijit.Tree(...);

Using the New Module Format

While you can continue to use the legacy module format and the legacy module API is the supported format, it is possible to start using the new module format with proper precautions, allowing you to take advantage of asynchronous module loaders including RequireJS or Backdraft now for faster and better module loading.

However, first a word of warning. The refactor to AMD is a transitional step for Dojo, and user-authored AMD modules are not supported within Dojo’s loader/build system yet. The current build system in 1.6 has very limited support for the AMD format. If you write your modules in AMD format and you still use the default synchronous Dojo loader, make sure you follow the same style (including line breaks) as the Dojo modules to ensure proper builds. The build system was minimally altered to build Dojo’s modules.

There is no official support for building with AMD modules, but of course it works if you follow the module declaration style of Dojo’s modules. The build systems provided by RequireJS or Backdraft are specifically designed for AMD, and much more robust and capable for the new format.

Again, Dojo 1.6 doesn’t officially support user-based AMD modules or asynchronous loading, but various users have successfully tested this functionality, and this is the future of Dojo. With Dojo, we do not break forwards-compatibility in dot releases, e.g. 1.5 to 1.6. We’re currently adding functionality that will be the foundation for Dojo 2.0, without breaking our promise of forwards-compatibility.

Creating a module with this format is simple:

  • define your dependencies in an array as the first argument
  • and provide a callback function to execute your module once the dependencies are ready
define(["dijit/Tree"], function(Tree){
  // The dijit Tree is now available, and we can use the local variable Tree
  new Tree(...);
});

Now your module is ready for a Dojo async loader or RequireJS. You can still use this module with standard dojo.require() calls as well.

To summarize your options:

  • Use the legacy format (dojo.require()/dojo.provide()) – These modules will only work with the Dojo sync loader, but they will build properly in the Dojo build system.
  • Use the AMD format (define()) with Dojo sync loader – These modules will load properly and can be used with other module loaders. This is not officially supported yet and they will only build properly in the current Dojo 1.6 build system if you carefully follow the Dojo modules code style.
  • Use the AMD format (define()) with RequireJS or Backdraft – Modules should load properly and can be used with other module loaders. They will build properly using their provided build system (however Dojo hasn’t been officially tested on other module loaders)

Referencing Modules

When using Dojo with the AMD format, there are also a few different ways to reference modules. First, the AMD recommended way of referencing modules is to declare them in the dependency list and then provide matching arguments, like we did above. However, this is not supported by the Dojo 1.6 build system. This module format is only for fully AMD-compliant loaders. An example:

define(["dojo/cookie", "dijit/Tree"], function(cookie, Tree){
  var cookieValue = cookie("cookieName"); // get a cookie
  new Tree(...); // create a dijit.Tree
});

This provides a big advantage over the nested-object-as-a-namespace approach to reference constructors and functions from modules since we no longer need to type out the namespace on every reference. We only need to give the full path “dojo/cookie” in the dependencies, and once it is aliased to an argument, we can directly reference by that argument/variable (cookie in this example). No longer does using Dojo mean typing “dojo.” until your ring finger develops carpal tunnel.

However, for those who need to use the Dojo build system, want to maintain current coding style, or want to migrate existing modules to the new format, we can also use “dojo” and “dijit” as dependencies themselves, for easy nested-object style referencing. Remember though, that we still need to list our dependencies, even if we don’t use the reference to them. The example above could be written:

define(["dojo", "dijit", "dojo/cookie", "dijit/Tree"], function(dojo, dijit){
  var cookieValue = dojo.cookie("cookieName"); // get a cookie
  new dijit.Tree(...); // create a dijit.Tree
});

Of course this is more verbose, but it makes migration much easier.

Another thing to be aware of with Dojo’s move to AMD is that this new format won’t work with previous versions of Dojo. Consequently, you can’t just copy Dojo 1.6 modules into an earlier Dojo release and expect it to work. In order to run 1.6 modules in an earlier version you would need to port them to the legacy dojo.require/dojo.provide API (and, of course, deal with any other dependencies from 1.6 that the module had).

Running Dojo on RequireJS

Dojo runs with RequireJS, using RequireJS as the module loader. To do this, first load RequireJS, then configure Dojo as a package:



And we can now load our application entry module, which can refer to “dojo” as a dependency:


Where my-module may look like:

define(["dojo"], function(dojo){
  dojo.query("some-query")...
  ...
});

There are a few things to be aware of when using RequireJS with Dojo:

  • RequireJS is managing module loading, so Dojo cannot automatically determine when to parse your HTML for declarative components/widgets. You must manually call dojo.parser.parse() after all necessary modules are loaded and the page is ready.
  • RequireJS is handling module loading, so you should also use RequireJS to do your builds.

The Future is Here. The Future is Near.

We will provide more information in the future on using the Backdraft loader, as we look to integrate it with Dojo.

We are also working on a package installer that will automatically generate your configuration setup.

We are looking forward to great improvements in module loading and interoperability with this new format. This will be a key part of our move towards autonomous packages as well. Look for more updates in the near future, and let us know if you would like to get involved.