Categories

Related tags

Modularizing and Packaging JavaScript for Scalable, High Performance Web Apps

07 November 2014

Modules in JavaScript

With the advance of MV* frameworks you can observe as JavaScript evolves ahead of official specs towards large scale development. It requires even higher level of maintainability and one of the first focuses here is encapsulation and information hiding. We need the code-base to be organized into independent, interchangeable components so that each of them contained code implementing only one aspect of desired functionality. Here comes to help the Module pattern. Module is a portion of code enclosed with a scope where all the members have private access by default and do not pollute global namespace. To get access to a member of module we have to export it within module scope and import it outside of module.

Prior to Ecma-262 Edition 6, which is still a working draft, JavaScript had no build-in facilities to define a module. However the concept is easy achievable by moving the intended code into a closure. That is usually self-calling lambda-function that imports passed-in parameters and exports an object in return statement. As you can see, for instance, in Ben Cherry's and Todd Motto's studies, here can be a number of implementations. However there are also standardized approaches: CommonJS Modules/1.1 and AMD. The first one was initially designed for server-side JavaScript and synchronous by nature while the second describes the way to deal with asynchronously loaded modules and their dependencies. AMD is meant to improve performance of in-browser web applications by bypassing module loading along with the rest of the page content. Yet, in real-world applications the core experience can require plenty of modules what produce quite a number of HTTP requests during page loading. One of the best practices for speeding up a web site implies that this number must be minimized. Developers of AMD loaders are aware of this and provide module file combining utilities (e.g. r,js). Such tools usually concatenate and compress module files. So you still need to load in you production code usually quite weighty AMD library and, now, synchronous modules collected in a single file, still designed for asynchronous loading. It doesn't sound as an optimal way, does it?

Fortunately in like manner of AMD optimization we can compile CJS modules in a single file suitable for in-browser use. It takes surprisingly little extra code to resolve synchronous dependencies as it is described in CJS specs. Let's see see how it works.

Debugging compiled code

It's usually hard to trace back the problem code in the sources files when an error occurs in the compiled code. However nowadays browsers support so-called source maps, so you can make JavaScript console point directly to the problem code:

$cjsc foo.js build.js --source-map=build.js.map

Run-time configuration

You may find among advantages of AMD configuration settings (including path aliases) to simplify path resolution and dependency listing. That is available with CommonJs compiler either.

For the jQuery module, we instruct the compiler to make a module from global property jQuery of window global object. For the plugin, we define path alias (./config/vendors/jquery.plugin.js) and specify that plugin's module must retrieve a jQuery instance from jQuery module and export it after modification (jQuery.fn.plugin).

Build automation

Building a web project is becoming a common thing. By using an automated build tool (ANT, Grunt, Gulp) we are used to run various tasks on project including CSS pre-processing, image optimization, JavaScript linting, source compression. Grunt task configuration for CommonJs compiler can look like that:

In conclusion

Comparing to AMD CommonJS modules are easy to maintain, it provides cleaner more readable syntax. Compiled CJS modules may give better end-user response time for the core experience code. However if your project requires modules loaded on-demand going without AMD loader can be a headache.

Both AMD and CJS/1.1 are put in the proposal of CommonJS Modules/2.0 specification. Some day we are going to have synchronous and asynchronous modules natively in the language. Until then we have to choose between an AMD loader and a CJS compiler depending on specific requirements of the project.

JavaScript applications have been growing in size and complexity increasingly over the past years. That brings new requirements for code maintainability where encapsulation gets crucial. While AMD implementation of modular design in JavaScript is wide-spread, CommonJS Modules/1.1 is are mostly used for server-side JavaScript only. Nonetheless this standard can be applied beneficially in-browser a well. Precompiled CJS has its advantages. Besides cleaner syntax comparing to AMD approach it may boost the performance of one's application by reducing the number of HTTP requests while the code stays well-grained. Here we are going to talk on how to achieve an efficient JavaScript code design by using CommonJS compiler.

Who's the dude?

Dmitry Sheiko is a web-developer living and working in Frankfurt am Main, DE