2011.09.30

AMD is better for the web than CommonJS modules

I’ve seen a few libraries and tools later using different kinds of ways to
handle dependency management, some of them are very similar to the way that
CommonJS modules looks like:

//use another module
var myLib = require('myPackage/myLib');

function foo(){
    console.log('foo');
    myLib.doSomething();
}

//expose module API
exports.foo = foo;

The beauty of CommonJS modules is how simple they are, you simply require something synchronously and that module can be used right away (just like magic).

The same code written in traditional AMD (AMD modules are flexible and can be written in different ways) would look like:

define(['myPackage/myLib'], function(myLib){

    function foo(){
        console.log('foo');
        myLib.doSomething();
    }

    //expose module API
    return {
        foo : foo
    };

});

Edit: Or even using this “simplified CommonJS wrapper” syntax:

define(function(require, exports){

    var myLib = require('myPackage/myLib');

    function foo(){
        console.log('foo');
        myLib.doSomething();
    }

    //expose module API
    exports.foo = foo;

});

In my opinion AMD modules are way better for the web right now than CJS modules, the asynchronous nature of AMD make it slightly more complex but it also expands the AMD power to a level that CJS modules can only dream of

AMD advantages

  • AMD modules are flexible.
  • Plugin support (extremely useful and powerful).
  • Can load more than just JavaScript files.
  • Path aliases and other advanced config settings to simplify path resolution and dependency listing.
  • Works in the browser without a build (most popular AMD loaders supports this feature).
  • Is asynchronous by nature.
  • Works in current browsers, no need to wait for Harmony.
  • Dependencies are usually listed on the same location making it easy to identify what are the dependencies.
  • Avoid globals by default since modules are wrapped by closures.
  • Can run the same code on both environments by simply using an AMD loader that works on a CJS environment (see r.js and amdefine).
  • It’s being adopted by popular JavaScript libraries like Dojo (1.6+), Mootools (2.0), jQuery (1.7)
  • Lazy-load scripts if needed.

Examples

Path alias to simplify module look-up and file versioning (really important feature):

//configure RequireJS/curljs paths
require.config({
    //set base folder of all dependencies
    baseUrl : 'js',
    paths : {
        //avoid typing long path all the time
        'foo' : 'lib/lorem-ipsum/dolor/foo',
        //version file for cache busting
        'lorem/ipsum' : 'lorem/ipsum-v23'
    }
});

define(['foo/bar', 'lorem/ipsum'], function(bar, ipsum){
    //this will load 'js/lib/lorem-ipsum/dolor/foo/bar.js'
    //and 'js/lorem/ipsum-v23.js'
});

Using plugins to load different kinds of dependencies:

//using plugins to load different file types
define([
        'text!./lipsum.md', 
        'image!img/lol_cat.jpg'
    ], 
    function(lipsum, lol_cat){
        console.log(lipsum);
        document.body.appendChild(lol_cat);
    }
);

Dynamic module loading + returning other types of data:

define(['require'], function(require){
    var mods = ['foo/bar', 'lorem', 'dolor'];

    function loadModuleByIndex(index){
        //dependency should be an Array
        require([ mods[index] ], function(m){
            //let's assume all modules have an init() method
            m.init();
        });
    }

    //modules can return any kind of data (string, object, function, etc..)
    return loadModuleByIndex;
});

Notes

The main reason for writing this post was a tweet by John Hann:

the more “universal module boilerplate” i see, the more i want to go with CJS 1.1.1 format. but then again, AMD has way more flexibility. hm – @unscriptable

And also because I’ve seen that some new tools and libraries are trying to mimic the CJS module format (I’m looking at you ender.js) or that have some special notation to include other files during build (google closure, sproutcore, …) and I feel they are going to the “wrong way” since AMD is clearly more flexible and becoming more popular each day.

I’ve been using AMD (RequireJS) for almost 1 year and can’t imagine going back to the era of complex namespacing (MyApp.something.bar), awkward script concatenation (which can usually result in problems if concatened on the wrong order), adding multiple script tags to the HTML, not being able to load scripts on demand, explaining to eveyone that they should wrap the code inside a self executing function to avoid generating globals, not being able to easily share code between projects, etc…

Don’t enforce a build step during development (even if automatic), one of the beauties of developing JavaScript is that you can simply refresh the browser to see the updates, run the build task only for deployment (combine and minify files to reduce number of requests and increase load performance).

AMD greatest benefit isn’t being able to load scripts on-demand, as some people may think, the greatest benefit is the increase of the code organization/modularity and also the reduced need for globals/namespacing.

More

RequireJS and curl.js are the most popular AMD loaders, check them out and use r.js to optimize your AMD modules into a single file and do some pre-processing of external resources before deploy. r.js can also be used to run AMD modules inside node.js and Rhino and to convert CommonJS modules into an AMD compatible format.

RequireJS mailing list is a good place to ask questions. There is also the AMD-implement list where different AMD loader implementors are discussing about features and how things should work to increase compatibility of the code.

James Burke (RequireJS creator) wrote a good post explaining why AMD is a good module format and why we should avoid the “bikesheding”, read it if you considering other module formats.

Check also a really good presentation by Brian Cavalier and John Hann post (both created after my post).

PS: when I started coding JavaScript as my main programing language I really missed “Classes”, now I see that what I was really missing was being able to split my code between multiple files “in a sane way” and to share code across different applications, inheritance is almost unrelated with that… (composition > inheritance)

Embrace AMD and be happy

Edit 2011/09/30: added “simplified CommonJS wrapper” suggested by James Burke and also renamed packages suggested by John Hann. Also removed comments about node.js supporting basic AMD modules since it seems they removed the native support.

Edit 2011/10/06: added link to John Hann’s post and Brian Cavalier’s presentation.

Related


Comments

A "simplified CommonJS wrapper" form is also supported by many of the AMD loaders, if people want something close to the CommonJS format, and is more lightweight than your third example:

define(function (require) {
    var myLib = require('path/to/myLib');
    return moduleValue;
});

If you want to use CommonJS exports and module:

define(function (require, exports, module) {
    var myLib = require('path/to/myLib');
    exports.foo = 'foo';
});

In these forms, the AMD loader will toString the function and scan for the require calls, load and execute those dependencies then call this function. More info here: http://requirejs.org/docs/commonjs.html

Feel free to remove this comment if you end up converting your third example.

awesome writeup Miller.

I do think it would be ideal if other environments embraced AMD, too. It's so awesome not to have to build in order to use: only build when you want to deploy. All your other reasons are spot-on too.

One thing I would change: you module id says "path/to/myList". I'm trying to dissuade people from mixing paths with packages. The fact that module/package ids look so much like paths (and act so much like paths in typical use cases) causes trouble as soon as people get into not-so-typical use cases. Id' put something like "myLib/myModule" or "myPackage/myModule". I plan to change all my examples to match that, too.

Regards,

-- John

Some people may argue that loading non-javascript resources should be done in code, rather than in the loader. Having done it both ways, I disagree. Why would I want to have a second, possible custom-coded mechanism to do my loading? Especially in the browser. If I need a template, a css file, a js controller, and a json file of translated strings to build my view, then it's so dang easy to just list them as dependencies.

[...] benefits in the browser world. For a more complete list, check out Miller Medeiros’s recent blog article on why AMD is better. I couldn’t say it any better than he [...]

lol, this is a bold article :)

I think that saying AMD is clearly more flexible than cjs is a bit of an overstatement.

I'd argue that there is a time and a place for both module systems and amd's async nature can actually often be detrimental to performance. People often jump in without really understanding resource management on the web - which makes amd a pretty dangerous contender.

Most serious development with AMD should probably be using something like r.js or loadbuilder to build files back together before you actually push anything to production. (So again, to be fair, it works in the browser without a build, sure, but eventually a build is still necessary.)

In a perfect world, i'd like to see people developing "modules" to export to each spec, it's not very hard... ryan florence actually came up with a pretty clever technique which looks something like this:

!function (name, definition) {
  if (typeof define == 'function') define(definition)
  else if (typeof module !='undefined') module.exports = definition()
  else this[name] = definition()
}( 'packageName', function () {
  return packageCode
})

at any rate ... anything is beter than $.fn.foo :D

cheers!

I forked Ryan Florence gist a couple days ago (and mine is more complete), and he definitely isn't the first/only one to do it.. I've been using a similar approach on Crossroads.js and will keep using it as long as it is necessary, I just think that for the client-side the AMD format is more flexible and have an easier development process.

Yes, people should definitely use r.js to combine all files before deployment (and I've been doing it on all projects), but being able to test things without the need of a build step is very helpful. As James Burke sent today on twitter: "AMD. Because the web already has a build step: F5".

Cheers.

PS: I need to fix my code formatting inside comments, right now it really sucks, only realized yesterday.

Hey Miller, great article!

I'm inclined to agree - at demand analytics, I'm using RequireJS religiously for our front-end app and it's working very well.

Having said that - I've seen libraries (such as underscore.js and co) having a module.exports section, which add a few bytes but enable them to be used in nodeJS with the require syntax. That seems to me like a good trade-off.

I'm thinking about including the same structure in accounting.js as well as another library I'm tinkering with at the moment.

My thoughts are that I'd like to use accounting and this other library in Node with the require syntax, but I'd also like to use them in our frontend app with RequireJS (without having to pull them in as standard javascript files)

So I suppose a library needs to be functional as a standalone JS file, a CommonJS module (for nodeJS) and a RequireJS/AMD module for script loaders..

The CJS module export can easily be included without adding too many bytes (see https://github.com/documentcloud/underscore/blob/master/underscore.js#L51) and without affecting the functionality as a standalone JS file - but the AMD module with define() syntax seems like it needs to be part of a build process that creates an extra file eg myLibrary-amd.js.

What do you think?

Hi Joss, the AMD export can be easily created if the file doesn't have any dependencies, check my pull request. I've been using a similar approach on my libraries but I really wish I could just distribute in the AMD format.. cheers.

Something I don't undestand: When you do define(['path/to/myLib'], function(myLib){ return {a:function(){}}; });

How is the returned value saved? I'm more farmiliar with this syntax which is clearer on this subject: define('myObj',['path/to/myLib'], function(myLib){ return {a:function(){}}; }); Where myObj will be then defined target.

Arieh, the examples are using the unnamed module format which is later translated to a named module during the build step, it increases code portability and enforces good practices (you can only have a single unnamed define per file), RequireJS documentation have more info about it. Cheers.

[...] there have been some blog posts lately that compare the pros and cons of each of them, so I don’t want to repeat all of this – [...]

[...] AMD is better for the web than CommonJS modules [...]

[...] (Nov 9): **: found an interesting post comparing the CommonJS way of defining modules to the AMD (RequireJS) way. I agree with the general [...]

[...] 本节将介绍如何支持“simplified CommonJS wrapper”模块,node.js的SJS模块实质在内部包一层构成一个SCW模块,而seajs提倡的 CMD 也源自于它。 [...]

[...] AMD is better for the web than CommonJS modules Tags: amd, best practices, javascript, requirejs Comments (0) [...]

[...] http://blog.millermedeiros.com/amd-is-better-for-the-web-than-commonjs-modules/ [...]

[...] 本节将介绍如何支持“simplified CommonJS wrapper”模块,node.js的SJS模块实质在内部包一层构成一个SCW模块,而seajs提倡的 CMD 也源自于它。 [...]

This seems to have been written without a thorough understanding of CommonJS in the browser. For example, Browserify has gained popularity. I've written about it in "Programming JavaScript Applications" (O'Reilly), which demonstrates the AMD alternative, as well. Having used both, I definitely prefer CommonJS. As another commenter noted, if you do anything serious, you're going to need a build step anyway, Grunt is a great build tool, and it can watch your files and automatically reload -- so you don't even need F5!

The promise of async as it turns out is less useful than the AMD hype would have you believe. I do think there's a place for async loading -- it just isn't for loading every dependency.

I'll almost certainly be giving a talk on why I think more people should be using CommonJS in the browser -- and show an example of how it works well for serious web applications.

I am grateful for Require.JS and AMD for pointing out that we really need a module format that works in the browser -- but as it turns out, we had one all along, it's quite well established, and it doesn't have any of the problems of AMD:

  • Too verbose
  • Awkward conversion steps to use CommonJS modules
  • The split personality (Is it async, or do we build? Trying to be both makes it bad at both.)

  • Eric

@eric I agree that AMD have it's own set of problems, it's like everything in life, there are always drawbacks. I've been writing non-trivial apps (more than 40K LOC, 500+ modules) and it surely paid off to be able to lazy-load certain parts of the app. Instead of loading ~800KB of JS upfront I loaded ~100KB and lazy-loaded the remaining as needed. But on most projects a single JS file for deploy is better, for these cases you can use almond which is a very small AMD loader. We should always do a build before deploying the project, reducing the amount of requests is fundamental for front-end performance.

One of the things I like on AMD is that during development you don't need a build step, break points and error messages will work fine without the need of source maps (which means it will work on IE dev tools if needed). The ability to load scripts from a different domain (eg. CDN) is also fundamental in my opinion.

Flexibility isn't always a good thing and comes with it's own set of problems (steeper learning curve, too many configuration options, verbose) but it can also prove itself to be extremely useful in some cases. Since my requirements changes very often and I work on multiple kinds of projects I prefer to be ready for the unknown.

The verbosity can easily be solved with code snippets for your editor - I only type def and press ctrl+tab and I'm good to go - we should also try to keep the amount of dependencies per-module as low as possible, dependency injection and decoupled modules are still a good thing.

The ability to list templates as a dependency is often overlooked, being able to pre-process the templates during build and load them dynamically during dev without any special setting is priceless. Every template is an individual file during development which helps a lot on the organization of the project. See handlebars plugin.

If browserify works for you that is a good thing, keep using it, but if someday you need something more flexible it won't be too hard to do the switch, there are tools to automate the module format conversion (see: r.js and nodefy).

Module systems are here to stay, be it AMD, CJS or Harmony, pick the one that suits your needs and be happy.

Cheers.

The key for me is that AMD tries to do more than it should - It is over complicated and awkward because it tries to handle both loading and dependency resolution. And as you've already mentioned, it also throws in some extras, like loading non-script dependencies.

Nothing about using CJS modules prevents you from using scripts loaded from different domains -- you just don't compile those into your bundle (and you don't compile them into your AMD module, either). I'm not sure why you consider this a crucial point. The difference is pretty trivial, as I see it.

Both systems can make use of scripts which are hosted remotely. The mechanics of it comes down to semantics from where I'm sitting.

Am I missing something about AMD? You talk about lazy loading part of the app, but not all of it. If you're using AMD semantics, you're either lazy loading all of it, or your running a build and compiling all of it, unless you use conditional requires to defer loading. If you're doing conditional requires, the difference between using that for your lazy loading, and using CommonJS + lab.js for lazy loading also seems a bit trivial.

"One of the things I like on AMD is that during development you don't need a build step, break points and error messages will work fine without the need of source maps (which means it will work on IE dev tools if needed)."

In my opinion, this is your strongest point, and the biggest strength of AMD, but as you mentioned, source maps are coming to alleviate the problem of debugging a built codebase. The fact is, what you're deploying is going to be built -- unless you never have bugs in prod, you'd better get used to debugging it.

As for templates, you can require those just like you'd require a piece of JavaScript. See https://github.com/edmellum/browserijade and https://github.com/mklabs/templatify for examples of how you might go about it.

It boils down to this: It's not Browserify's job to figure out how to get your templates into your scripts. Browserify is great because it's simple. Node modules are great because they're simple.

AMD gets in trouble because it tries to do things that it should not try to do.

Some people don't mind a little trouble though. RequireJS is a well written piece of software engineering, and AMD does indeed have some handy features. If you don't mind the way it pollutes every single module with obnoxious boilerplate, or how it pressures library authors to support yet another module format (as any cross-stack module is already supporting browsers and node modules), that's your choice.

Douglas Crockford said something funny once, when he was asked what he thinks about JSHint. It went something like this: "There are a lot of stupid people out there. It's nice to know that there's a tool for them."

I'm tempted to say the same thing about AMD, but I personally love JSHint and use it instead of JSLint in my build process. One of the most important things that Douglas Crockford has to teach is that you can't be right about everything, and different people like different things for different reasons. I'm not going to say that AMD is wrong -- just be prepared for disagreement when you say it's "better" than CommonJS.

Cheers!

Eric

[...] JavaScript Namespacing Patterns For Large-Scale JavaScript Application Architecture AMD is better for the web than CommonJS modules [...]

Mikhail Davydov

There is another module system called LMD: Lazy Module Declaration that uses CommonJS modules and AMD with plugin.

Before I start to write coments for each AMD advantage I say that "builders are averywhere, they are part of every modern application, they hels a lot".

AMD modules are flexible.

Same as CommonJS

Plugin support (extremely useful and powerful).

require() is IMO a God function (an anti-pattern) that can return all kind of resources.

LMD way


require.async('module', callback);
require.css('module', callback);
// or Promise way
require.css('module').then(callback);
require.js('module', callback);
require.yourResourceName();

Can load more than just JavaScript files.

Same for LMD. And as for me RequireJS text plugin is odd thing require('text!text'). Text is common resource U Y NO require('text').

Path aliases and other advanced config settings to simplify path resolution and dependency listing.

Same and I think, LMD has more powerful things like glob match with interpolation.

Works in the browser without a build (most popular AMD loaders supports this feature). Is asynchronous by nature.

build is not advantage IMO

Works in current browsers, no need to wait for Harmony.

Same

Avoid globals by default since modules are wrapped by closures.

Same. And with LMD globals are crystal clear - no require in globals nor define. LMD build is selfcontained.

Can run the same code on both environments by simply using an AMD loader that works on a CJS environment (see r.js and amdefine).

LMD is CommonJS by default.

It’s being adopted by popular JavaScript libraries like Dojo (1.6+), Mootools (2.0), jQuery (1.7)… Lazy-load scripts if needed.

LMD can eat modules without adoptation at all. LMD require('$') will search for $ at "abstract FS" than at globals.

Please take a look https://github.com/azproduction/lmd

@Mikhail there is no reason to create a new module format, see James Burke post "On inventing JS module formats and script loaders".

You are missing the point on async loads/plugins. I want my templates to be loaded as a dependency before any code gets executed, it is a dependency that should be resolved before the module definition. This reduces drastically the amount of callbacks/promises needed inside the app code (see @unscriptable comment).

Cheers.

[...] all about modular javascript, as well as an interesting debate on the proper method of defining modules, but the RequireJS site itself has enough info to get a basic project off the ground. I had to do a [...]

Only experienced and qualified Canada immigration consultants can look into your needs and ensure that you have chosen the right immigration status for your situation. That's a valuable exemption to investment expectations. He provides you with correct information about the employment opportunities in Canada.

Here is my page [иммиграция в канаду](http://www.youtube.com/watch?v=9zVtjqU020w "иммиграция в канаду")

Your style is so unique compared to other folks I have read stuff from. Thank you for posting when you've got the opportunity, Guess I will just bookmark this blog.

Also visit my page; [xbox games jb hi fi](http://www.asymptotix.eu/content/web-sites-offer-hidden-object-games-and-any-huge-selection-other-free-video-games "xbox games jb hi fi")

So why not gget ahold of us aat this moment forr the top gutter maintenance service that is second to none. But if your modified adjusted griss income is less than $100,000, you can use up to $25,000 in loses on a rental property to offset other taxable income including earned income, such as your salary oor wages.Since pricing and fee structures can be vastly different among property management companies, discussion with the manager as to the breakdown of each charge and carefu reading of the management contract is crucial in gathering information as to identifying the true cost of hiring a property management firm.

Visi my blog: [flats in panvel](http://youtu.be/iLpb-bqACyo "flats in panvel")

It's really a great and helpful piece of information. I'm glad that you shared this useful info with us. Please keep us informed like this. Thanks for sharing.

My blog post: homepage; [Bernadette](http://www.hischurchvt.org/mboard/msg/16162.html "Bernadette"),

I payy a quick visit day-to-day a few web pages and information sites to read content, but this website presents quality based posts.

Look at my homepage; [Brave Frontier Cheat](https://sites.google.com/site/bravefrontierfreehack/ "Brave Frontier Cheat")

Well, various strategies develop, when thhe question rises about how to ale your used cars. You definitely waqnt to avoid paying for what looks like a highly desirable car only to find oout that it is a clone.

Jean Sccheid has owned Ford, Chrysler, Dodge & Jeep dealerships in New Mexico.

Also visit my weblog ... [Dodge Neon Fort Lauderdale](http://youtu.be/KZg0yj1YSZc "Dodge Neon Fort Lauderdale")

Leave a Comment

Please post only comments that will add value to the content of this page. Please read the about page to understand the objective of this blog and the way I think about it. Thanks.

Comments are parsed as Markdown and you can use basic HTML tags (a, blockquote, cite, code, del, em, strong) but some code may be striped if not escaped (specially PHP and HTML tags that aren't on the list). Line and paragraph breaks automatic. Code blocks should be indented with 4 spaces.