Monday, July 30, 2012

Enterprise-class Javascript, part I



Recently a friend of mine asked my opinion about library called RequireJS. I have done lots of Javascript but still I haven't even heard about it. Shame on me. As an execuse I'm just saying I'm mostly server-side programmer and Javascript is just my hobby. After going through the RequireJS documentation I noticed that it's not just a regular Javascript library, but a Javascript module loader instead. I always overlooked CommonJS and things alike and thus I didn't even know about AMD (Asynchronous Module Definition). OMG, how it's possible I didn't know about it? So far I've thought that jQuery, maybe mated with underscore.js is enough. Just use things like namespaces, avoid globals at any cost, add JSDoc comments to your code and mangle it through Google Closure Compiler. With this approach you can have some kind of intellisense, loads faster and your code is theft-protected at least to some extent. But something was missing ...


After initial shock I did some tests with RequireJS. Worked as expected and I was really happy since I used to take care of namespaces and Javascript module pattern by myself. No more, since RequireJS takes care of it on my behalf. It was just what I've been looking for a while. Somehow it remainded me about OSGi. And just like with OSGi, it kind of forces you make modular code. Also, if you follow good programming practices you can hide your dependencies right to the place where belong and keeping your private functions private. Let's take an example. If the module is dependent on, let's say, underscore.js, I really don't care as far as it fullfils its contract. Or let's say a module is using backbone.js, which in turn is using underscore.js and jQuery. With traditional approach I have to have zillions of script-tags in my page using any of those libraries and they have to be in correct order. With RequireJS my module now looks like this:

// file uploader.js
define( ['jquery'],function( $ ) {
// Do this and that with jQuery
} );

and my module consumer now looks like this:

<script src="require.js"></script>
<script>
require( ['uploader'],function( Uploader ) {
var u = new Uploader( ... );
...
} );
</script>

Notice that I only have one script tag (besides my own inline script), which is loading require.js. If my consumer side code is using jQuery, I would also have jquery as depedency. Now it looks like this:

<script src="require.js"></script>
<script>
require( ['uploader','jquery' ],function( Uploader,$ ) {
$(document).ready( function( ) {
var u = new Uploader( ... );
...
} );
} );
</script>

But why load jQuery again? Isn't jQuery already loaded? Yes, maybe it is, but even though my uploader is using jQuery, it's just an implementation detail. You should always list all your dependencies and RequireJS takes care of asynchronously loading it just once (unless you want multiple versions).


Hiding dependencies to where they belong is really important thing. Think about adding a dependency to common code referenced by hundreds of pages. With RequireJS it's just a matter of telling the dependency and dropping a Javascript file to correct location.


And finally, if you think about how seriously you should take AMD, look inside your jQuery 1.7+. The AMD module definition is there.


This was just the beginning and more will come about manglers, optimizers, using globals, ... Meanwhile you should visit RequireJS site.

No comments:

Post a Comment