JavaScript MV* Framework - Making the Right Choice
JavaScript frameworks have been proliferating recently with a frightening rate. Just take a look at TodoMVC that provides dozens of MV* framework-specific implementation for a single task. It gives a felling how must be confused a developer making a choice for a framework.
Some time ago I even ran into a manifesto (http://bitworking.org/news/2014/05/zero_framework_manifesto) against JavaScript frameworks. I do not completely agree with the author, yet he has a point – it’s too many of them, too many of meta-languages to study that you as a web-developer may not need at all. Most of the frameworks contain plenty of code (AngularJS - 22K LOC, Ember.js – 43K LOC) and if you hope to know what is really going on in your application you have to study every line of this code. Let’s assume 3-5 of the trending frameworks are thoroughly tested by the communities, but what about others? Are you ready to spare days and weeks digging in someone’s code looking for traps and leaks in there? And, by the way, it’s likely you will give up eventually on one framework just to start again with another. Besides, these general-purpose frameworks often keep pretty much of code that you will never need in your projects. For example libraries like Underscore and Lodash consist of utility methods, which don’t make much sense when your minimal requirements are browsers supporting ES5. In addition to that dead code isn’t welcome in a project, it loads every time somebody visits your app.
So, are frameworks evil? Shall we avoid using them? Oh God, no! We just have to be careful while choosing one. Without a strict convention, without a guideline you are unlikely to keep your code consistent and readable across the team. Frameworks with a vary success help in there.
Inspired ages ago with YUI widget abstraction I kept to that paradigm in my Vanilla code for years. That proved to be quite sufficient for not big widgets, but whenever you need a complex module the logic encapsulation tends out of the pattern. My server side programming background starts to whine for MV*-like design and this comes with a framework.
What I really need is an ascetic light-weight solution that rather provides a convention to follow than a complete programming platform. Moreover, I want a well-known framework, widely used, well documented, supported by a huge community. Of the Big Three (Backbone, AngularJS, Ember.js) Backbone seems as the best choice. However it’s still has plenty of redundancies, especially these dependencies Underscore and jQuery that you don’t really need for ES5 browsers or when using ES5 shims. Of course I’m not the only one concerned about it. Some good people made Exoskeleton – an optimized version of Backbone that’s fine without dependencies and fits in just 8K (gzipped JavaScript). So it takes only a few hours to go through the whole codebase of the framework and get into what and how it really does.
Let’s do some practice to get a grip on it. Our test single-page application will simply show up a modal window with a content depend on hash provided in the URI.
Following the Backbone design we need a View object representing the modal window and a Model object handling View’s data. We need a router (./app/controllers/Front.js) that invokes the View for a requested page and App object doing application bootstrap.
I would prefer to keep all these objects in separate modules, so it will be CommonJS modules compiled by cjsc into single JavaScript file suitable for in-browser use.
As we build modal window dynamically it’s handy to keep its markup in a template (./app/views/templates/floating-page.tpl). While using cjsc you can obtain template’s content as a string by simply calling with require. So we only need to populate the placeholders in it with the intended content. In this simple case we could rely on String.replace, but in general templating must be done by a template engine and I will use Mustache.js. Why? Again, it’s widely-spread and pretty ascetic (https://github.com/janl/mustache.js/blob/master/mustache.js).
The only module I’ve not yet mentioned is ./app/misc/mediator.js. What is that? Backbone modules have PubSub methods, but there is no messaging bus to communicate between instances in isolated modules. It’s available in extensions (e.g. Backbone.Wreqr), but if we are to keep dependencies minimalistic, let’s simply add a module capable of registering an instance and propagate PubSub methods across the composition. Let’s say we have the Router that knows what page was requested. We have to pass this information to the View and make it to show the modal window. As we have the View registered to the bus:
mediator.register( "floating-page-view", new FloatingPageView({..}) );
we can do it simply casting an event to the View instance:
mediator.trigger( "floating-page-view", "show", "page-a" );
Within the View constructor we have a handler subscribed to the event, so it will be invoked as soon as we call it
initialize: function(){
this.on( "show", this.show, this );
..
},
show: function( pageId ){
..
}
So what is happening in the application? When a page requested (index.html#page-a), the router calls module:app/views/FloatingPage#show. The method displays an empty modal window (module:app/views/FloatingPage#render) and asks the Model for update. Model makes an XMLHttpRequest to the given URI (data/page-a.json) and updates. The View has render method subscribed for this event, so the modal window gets re-rendered this time with content from the Model. View has also a handler invoked whenever close button is called. The handler just destroys the modal window.
That is it. You can find the complete source of the example here https://github.com/dsheiko/exoskeleton-exercise.