ponyfoo.com

The Web Wars

Fix

There have always been wars in browser-land. Browsers, specs, politics, lots of politics. Even libraries had theirs.

What once was the browser utility library war, has now been settled, jQuery won that. We are now in the midst of another one, the framework war, with AngularJS leading the competition. There are lots to pick from though, and it isn’t anywhere near settled. Popular frameworks include BackboneJS, EmberJS, SproutCore, KnockoutJS, just to name a few.

Before Node, and frameworks like Angular, it wasn’t all that common opening a web project and finding front-end code organized in a way that scaled. This war will probably drag on for at least a couple more years. Maybe even indefinitely.

As we can see on TodoMVC, there are a boatload of different MV* frameworks out there. I expect that many of those won’t make it very far, while a few will gain more traction.

I’ll also be covering CommonJS vs RequireJS vs LazyJS, an alternative I developed.

angularjs.png
angularjs.png

I believe AngularJS has a good chance of winning. It’s a great combination of MV* patterns, great modularization. It’s debatable whether IoC will captivate developers at large, but they certainly implemented it in a clean and coherent way. Their solution is highly extensible, higly comprehensive, and, considering its extensive feature-set, extremely intuitive and easy to use. So, kudos Google!

However, it won’t enjoy the widespread adoption that jQuery has. jQuery is, ultimately, a utility library that’s meant to be auxiliary to your application, AngularJS is an integral solution, and it restricts how you can organize your front end architecture.

Lets get into RequireJS, a script loader that resembles the AngularJS dependency injection mechanism.

A little background history

There was a time when Node was coming up, when they had to decide on a spec to organize the complex code architecture that comes with a web application’s back end. The CommonJS Modules/1.1 spec was conceived. But it didn’t quite work in browser-land.

It wasn’t that long ago CommonJS and RequireJS took sides in a little skirmish. Fortunately, that’s more or less settled. To be fair, I created LazyJS in hopes to reignite that argument. I’ll expand on that later.

CommonJS Modules/1.1

Modules, as defined by the CommonJS spec, the so-called Node way, are directly related to the files that contain them, and can be included using require. A module will contain all properties published in the public interface defined in module.exports.

// util.js

module.exports = {
	public_api: true,
	log_message: function(message){
		console.log(message);
	}
};

// main.js

var util = require('./util.js');
expect(util.public_api).toBeTruthy();
util.log_message('foo');

// > foo

This pattern is very good for Node.

  • Everything that’s not exposed with module.exports is considered private to the module
  • Modules are interpreted once, and their results are stored for subsequent calls to require
  • It’s similar to what we see in other, non-prototypal, server side languages
  • Modules just execute and return a value. You are not tied to a particular pattern

RequireJS

requirejs.jpg
requirejs.jpg

AMD is a concession to the fact that browsers are just terminals and, unlike Node, assets are fetched over the network. This introduces a host of problems, such as latency, uncertainty (that the file will ever load), asynchronicity, and such.

AMD attempts to correct these issues by providing an asynchronous module loading pattern.

define('module', function(dep1, dep2){
	return function(){};
});

Benefits of using RequireJS include the following.

  • Modular code that isn’t limited to one module per file, like CommonJS modules are
  • AMD modules work even if they aren’t resolved immediatly, due to dependencies such a script in an external CDN
  • DI pattern. Dependencies are inferred from the arguments, resolved, and injected to the module function, all of which is done by RequireJS

Behind enemy lines

If you are like me, you don’t care about the politics behind different specs and solutions. All that matters is getting a clean solution that just works.

It’s a fact that CommonJS was not aimed at the browser, and although I prefer it for Node, I wouldn’t try and force it into browser-land. However, RequireJS attempts to do too much when it comes to the browser, and kind of fails at it.

You need a lot of boilerplate code in order to get AMD working, and it shouldn’t have to be that way.

RequireJS set out to be a simple and easy to use script loading solution, but that’s not quite what you find here.

RequireJS allows modules on the Object level, but I’m not so hot on the idea that my script loader should also take on the task of providing inversion of control and dependency injection mechanisms

Besides that, there are some unwanted complications. The example above won’t work when the JS is minified, because the arguments on the anonymous function will get renamed to something like a, b, meaning they won’t be able to infer the names of the modules anymore. The solution, is even more verbose.

define('module', ['dep1', 'dep2'], function(dep1, dep2){
	return function(){};
});

This leaves you wondering why they try so hard to provide something that just won’t work in production environments. And why does everyone have to modify their code to comply with yours? That’s just not right.

How is LazyJS any different?

  • Modules don’t need to adhere to silly conventions, you can you require them providing the /path/to/the/source.js
  • Faster, very little JS is parsed on page load, after that, necessary JS is parsed on demand
  • Less tightly coupled. Directives are comments that don’t define the way you should style your code
  • Less ceremony. Use the same tools and concepts in development and production
  • Succint. Give it a hint of which modules you depend on, that’s it
  • KISS

LazyJS is different in that it can become whatever you want it to be. It’s different in that it lets you organize blocks of code (entire files or chunks), and specify their dependencies.

For now it doesn’t even have an API, it’s just an idea. And I want to spend some time figuring out the best “out of the box” feature-set, without staining everyone’s code with extra code.

Suppose you want it to “become” CJS, then you should reference a bundle, disable AJAX calls, and let it resolve everything on its own before, synchronously, giving you a result back.

Similarly, you can leave it pretty much on its current state, expose the .lookup(url, done) function, and voilá, you’ve got RequireJS. Sort of.

Feedback regarding LazyJS is welcome, and I promise to try my best to leave it as agnostic and unopinionated as possible.

Liked the article? Subscribe below to get an email when new articles come out! Also, follow @ponyfoo on Twitter and @ponyfoo on Facebook.
One-click unsubscribe, anytime. Learn more.

Comments (7)

Andrew Schmadel wrote

What are your thoughts on browserify?

Nicolas Bevacqua wrote

I haven’t really used it, but from what I can tell it looks like an interesting choice in the browser.

I definitely prefer it to AMD modules. If that’s what you’re asking.

Update

Have used it, it’s freaking awesome.

Ditch Require.js, use browserify.

A. Matías Quezada wrote

Actually I like the CommonJS syntax of RequireJS it allows me to develop my modules with TDD over Node and then just adding

define(function(require, exports, module) {
  <...code...>
});

And then test and use them on the browser. Also it sets one dependency per line what makes it easier for version control and to extract directly a subpart of the module.

define(function(require) {
  var dependency = require('./module');
  var extend = require('underscore').extend;
  <...code...>
});

It is optimizable and very easy to reuse between node and the browser as long as you develop Javascript-only as I did on my promises implementation

Nicolas Bevacqua wrote

That’s fine with me if you’re developing something that’s required to conform to both Node and the browser, but that’s generally not the case for most efforts, where you’re either developing client-side code, or you’re working on Node.

In those cases, I’d much rather stick with CommonJS in the server and something a little less convoluted in the browser, such as exposing a global if needed, and working from there.

Have you considered writing CommonJS code directly, and then using browserify to translate your Node package to browser-land?

That would’ve have the added benefit of using your source code directly, in the case of consumers of your module’s Node version.

A. Matías Quezada wrote

Yeah, it’s mented to reuse code between node and browser but not only for developing it, also in case you are developing browser-only code and you need to reuse a node snippet is a good solution to have so I develop almost all my RIAs (except angularjs ones) using Require’s CommonJS mode.

I don’t like much browserify, it’s good for prototyping and experimenting but I will definitely not use it for production, it makes it so easy to import a big chuck of code without you knowing it. Long story short: it’s ment to use on the browser code than was not mented to be used in the browser.

The only case I don’t see a fit for RequireJS is with AngularJS since angular’s dependency injection does the half the job of RequireJS. Anyway it has a catch: to import a directive I have to go to the HTML, add the JS file, go to the JS, add the module dependency and go to the CSS and add a @import sentence. RequireJS is a little bit too big for this, but I think I will use some script loader so in order to import a directive I just load the JS file on my JS code and it should load the css it needs automatically.

Nicolas Bevacqua wrote

Regarding your last paragraph: in production you could just use a Grunt task to bundle and minify. In development you might want to use templates to avoid adding the JS and CSS files by hand.

To me, using JavaScript in order to load CSS is just plain wrong.

A. Matías Quezada wrote

Can you talk a little more about templates? is it an angular feature?

For me what is wrong is the need to load more than one file to use a dependency. I’ve writed an article (spanish) last week about it.

It’s simple, to load a web module (HTML, JS and CSS) you should use just one of this languages to import another file of the same language representing the module. Oviously CSS is not the tool, so what I mean is

  • to add a HTML tag to load an HTML file who loads/contains CSS and JS (see WebComponents)
  • or a JS sentence than loads a JS file who loads/contains CSS and HTML.

Until WebComponents are available on all major browsers the only way to do it is thugh javascript. Of course Javascript should not be the tool for this, HTML is the tool ment to load resources, but right now the right option is not available.