Are You Managing JavaScript Dependencies Like an Old Man?

Manage Tornado of JavaScript Dependencies Over the past decade JavaScript standards and best practices have improved but dealing with dependencies and modularity is difficult. Frameworks like Require.js (and others) remedy these issues and more. Don’t be mistaken, there are teeth and merit to this new shiny toy. Sit down, buckle up, and keep all hands and arms inside as we teleport through the JavaScript time machine!

Travel back to year 2000. JavaScript’s main existence was validation. There was no notion of namespaces and all code lived in global space. After the dark ages, during the year Revenge of the Sith was the #1 movie and Single Page Application (SPA) was coined by Steve Yen, libraries like jQuery, Mootools and Prototype.js gained traction. Other best practices took root around closures, Immediately-Invoked Function Express (IIFE 1, 2), and namespaces. Presently, almost everyone knows best practice (learn & Pluralsight ):

  • No augmenting built-in native objects
  • Use namespaces to prevent polluting global namespace ( Namespace.js )
  • Leverage IIFE
  • Use JavaScript Design Patterns – Module, Revealing Module, MVC, & MVVM.

Using namespaces isolates code, creating a sandbox of sorts to avoid name collisions and better organized code. The drawback to namespaces is verbosity and namespace cluttering. As applications grow deep nested namespaces (FrozenBytes.Common.ServiceProxy.doAjaxPost(method, ..)) are common, sparking an urge to put all methods into a single object. All methods on a single object are a red flag, name collisions will increase. Even using noConflict() is error prone. Race back to current day, 2013, the year of Iron Man 3 and release of VS 2013 forget all of this mess, instead work with a module system.

A viable option to support modularity in JavaScript is to leverage namespaces and IIFE. However, the dependency problem remains and JavaScript files must be referenced in proper order which is cumbersome and error prone. A better approach, leverage RequireJS, it implements clean, maintainable modern modular JavaScript patterns like AMD and CommonJS. In RequireJS code is separated into reusable modules targeting a single concern. Implement SOLID design principles) and decouple dependencies in your SPA or ASP.NET MVC JavaScript for FREE using the modularity of RequireJS. RequireJScan handle custom and third party dependencies no matter the nesting using configuration and shims. One could argue RequireJS provides basic form of dependency injection (DI) or Service Location. Say goodbye to deep nested namespaces, complex ordering of scripts and version difficulty. Instead, simply load require.js, add an entry point using main.js, add configuration for custom and/or third party scripts then begin using the require or define to asynchronously load scripts and modules. Implementing in a SPA is straight forward as seen below. What about multi-page applications? Before you say this won’t work in a traditional multi-page applications like ASP.NET MVC or even TypeScript, hold your tongue. While this is out of scope for this article please visit the references below or reach out to me via segilbert@deloitte.com.

Quick Start

Require.js quick start cheatsheet

  1. Bootstrap Require.js inside index.html by adding a script tag, then define the main entry point of the application using data-main HTML5 attribute, App/main.js.
  2. Configure Require.js using require.config, define properties, paths specifying versions and/or shims to load non AMD modules.
  3. Define a module with no dependencies, add an anonymous function called util to return the sum of two numbers. Best practice is a 1:1 mapping between a module and physical file; file named util.js to match the module.
  4. Define an app module with a dependency on util module. Use util module to return 2+3 inside the app.js run function. Require.js will load App.js in the main.js file.
  5. Wire-up app.js module with Require.js using the require keyword in mian.js. Behind the scenes Require.js loads, injects the dependency chain with proper scope relative to the root as dependencies to the function enabling execution of app.run.

A more complex example:

Durandal SPA Example

  • Define modular dependencies for the function. RequireJS will inject system.js, logger.js, router.js, and config.js relative to root as dependencies to the function.

To learn more I recommend reading Require JS Fundamentals, writing modular JavaScript and reviewing RequireJS documentation.

Code Sample

Pluralsight Courses

Articles

References

Comments