You are viewing the Articles tagged in requirejs

Function Modules in RequireJS

Having leveraged RequireJS as part of my preferred client-side web stack for some time now, I find it rather surprising how often I recall various features from the API docs which I have yet to use, and how they may apply to a specific solution I am implementing.

One such feature is the ability to define a module as a function. While at first this may seem a rather basic feature, and indeed it is, there are quite a few practical purposes in which returning a function as a module definition can prove useful.

Function Modules

As one may expect, returning a function as a Module in RequireJS is rather straight-forward. For example, a simple random function can be defined as a module as follows:

Then, the function module can simply be invoked as follows:

Function Modules as Factories

Perhaps a more practical example of implementing a function module is in the context of a Factory Method.

For instance, a function module can be used as a convenient means of creating a specific type of object on a clients behalf based on certain conditions.

Take (an intentionally simple) example of a function module which, given a specific role type, returns a corresponding view implementation (in this example, a Backbone View) for an Editor feature:

Client code can then simply invoke the factory in order to retrieve the appropriate Editor view implementation based on the specified role type:

Defining modules as functions which serve as factory methods can help simplify client code implementations, as the responsibility of determining the type of object to create, configure, etc., can be delegated to a dedicated object; thus allowing for simpler designs which better facilitate code reuse, testing, and maintenance.

As a general rule of thumb, I typically reserve implementing modules as functions for cases in which a package level method would be appropriate, or for factory implementations. However, there are other scenarios in which function modules may apply, and so they are certainly worth noting.

You can fork the above example here.

Basic Dependency Injection with RequireJS

Recently, I was having a conversation about the basic concepts of IoC/DI, and, specifically, how they pertain to modern (single page) JavaScript Web Applications. This discussion was quite interesting, and so I felt inclined to share some thoughts on the subject with a wider audience.

Dependency Injection in JavaScript

Being a dynamic language, when designing JavaScript based architectures, in comparison to architectures which are under the constraints of a statically typed language, one is typically less inclined to consider the relevance of, or immediate need for, a complete IoC container. Of course, context is key, and so there are certainly JavaScript applications which can benefit from an IoC container (such as wire.js). As such, it would not be prudent for one to suggest otherwise; but rather, this is simply to say that for the majority of JavaScript applications, standard AMD loaders provide a sufficient means of managing dependencies; as the rigidness inherent to statically typed languages which IoC containers help manage are generally less relevant to dynamic languages.

With that being said, while a robust IoC container may not be necessary for the majority of JavaScript applications, it is quite important to emphasize the benefits of employing basic dependency management and Dependency Injection; as this is an essential design characteristic which is critical to the success and overall maintainability of large scale client-side web applications.

Facilitating Code Reuse

Anyone who has been responsible for developing and maintaining specific core features across multiple applications is likely to understand that the ability to facilitate reuse of JavaScript modules is crucial. This is particularly important in the context of architectures which must account for the ability to support mulitple implementations of the same application across different form-factors; for, the ability to manage and configure dependencies can prove paramount; allowing for a framework upon which various form-factor specific implementations of an application can be supported.

In addition to this, as one might expect, having the flexibility necessary for configuring dependencies lends itself, quite naturally so, to various unit testing scenarios.

Configuring Dependencies with RequireJS

Though not always immediately apparent, applications leveraging RequireJS are essentially using a basic form of Dependency Injection out of the box – even if not in the most purist sense of the term. However, the simple matter of mapping module names to module implementations can be considered, in-of-itself, a basic form of Dependency Injection, or perhaps, one could argue this as being more of a Service Locator, as RequireJS does not instantiate dependencies on a clients behalf. Regardless of the preferred classification, this mechanism of defining dependencies is quite important, as it affords developers the ability to change module implementations as desired without the need to change client code. Of course, such modules must adhere to a specific contract (interface) so as to ensure clients which depend on specific named modules receive the expected API.

Explicit Dependencies

Consider a rather contrived example of a shared Application module which is used across two separate applications; one for Mobile and one for Desktop; with the Application module having a dependency on an AppHelper module:

Both the Mobile and Desktop applications can easily map the AppHelper module to a context specific implementation via their respective main.js configurations:

Based on the above, it is rather evident that the AppHelper module is mapped to the appropriate application specific implementation; MobileHelper for mobile, and DesktopHelper for desktop. Additional context specific APIs can just as easily be defined, and thus provided as dependencies to other modules as needed using this very simple pattern.

Implicit Dependencies

Dependencies need not always be explicit, but rather they can also be implicitly mapped based on the path to which each application’s main.js configuration resides, or based on the configured baseUrl path.

For instance, given the above example, we can map a Templates Module, and implicitly inject the path to each context specific template based on the application’s default path, or baseUrl path:

As can be seen, each application’s main.js defines a Templates module and a TemplateSource module, respectively, with each being shared amongst both the Mobile and Desktop specific applications. The Templates and TemplateSource modules are defined as follows:

While both the Mobile and Desktop applications may share the same Templates and TemplateSource modules, the specific implementation of the templates loaded from TemplateSource is determined via each application’s base path; thus, the path to app/templates/some-view.tpl automatically resolves to the context specific template; i.e.: mobile/app/templates/some-view.tpl for Mobile, and desktop/app/templates/some-view.tpl for Desktop.

Concluding Thoughts

While the above examples are rather basic, they do serve well to demonstrate just how easily one can design for module reuse across different applications with RequireJS, which itself allows for much more robust configurations of modules; such as loading context specific modules at runtime, augmenting modules for differing contexts with mixins, providing third-party libraries based on a particular form-factor (e.g. jQuery for Desktop, Zepto for Mobile, etc.), and more.

You can clone the above example here.

Organizing Require JS Dependencies

When developing large scale web applications leveraging RequireJS, at times, even the most highly cohesive of modules will require quite a few other modules as dependencies. As such, maintaining the order of these dependencies can become somewhat tedious. Fortunately, RequireJS provides a means of simplifying how modules may define dependencies for such cases.

Ordering Dependencies

If we are to consider how a typical module definition specifies dependencies, it becomes clear that one must ensure each module dependency name and it’s corresponding definition function argument have been listed in the same order:

In the above example, jQuery, Underscore and Backbone are specified as the modules dependencies as a dependency names array passed as the first argument to define(). Once all dependencies have been loaded, the modules definition function is invoked; with each dependency passed in the same order in which they were defined in the dependency array.

From both a design and client implementation perspective, this one-to-one correlation between dependency ordering and definition function argument ordering makes perfect sense, of course, for it would obviously be extremely confusing otherwise. In general this is rarely a concern, though when a module has many dependencies it can become cumbersome.

Adding Dependencies

The necessary side effect of the dependency/argument ordering is that as other dependencies need to be added, time must be spent ordering and re-ordering dependencies if one takes care to group dependencies categorically in order to improve readability (e.g. models…, collections…, views…, etc.).

For example, consider the following:

If we were to decide that this module also needed, say, Handlebars, we could simply add the new dependency to the end of the dependencies array, and then just add it to the end of the factory function’s arguments as follows:

While the above approach will certainly work, it fails to aid in readability as Handlebars is grouped with the application specific dependencies – the Model and Collection, as opposed to being grouped with the module’s framework dependencies. This may seem like a trivial detail, however, considering code is typically read many more times than it is written, it makes sense to organize dependencies as they are added in order to save ourselves and others time in the future when viewing the dependencies.

And so, ideally a team would have an established pattern of grouping dependencies in some kind of logical order. For example, framework specific dependencies could be listed first, followed by application specific dependencies etc. This ordering could be as simple or complex as a team collectively decides, though I would recommend keeping it generally simple.

With this in mind we could improve the above example as follows:

Organizing Dependencies

If we are to consider the above example as being somewhat typical, then it becomes rather clear that with each new dependency added we will likely have to repeat the ordering process. Again, while this may seem insignificant, it can easily lead to exceptions being thrown if any dependencies are out of order.

Fortunately, RequireJS provides a simplified CommonJS wrapping implementation, or Sugar syntax, which can be used to solve such issues. This sytax (which will feel natural to those who use Node) allows one to simply provide a module’s definition function to the module’s define call, and specifiy require as a single argument, as follows:

Using this pattern, we can refactor the above example to be more easily managed as follows:

With this pattern of dependency mapping it becomes much easier to add and remove dependencies as needed, with the added benefit of reading much more naturally. This pattern also feels more familiar as it is similar to import directives in other languages.

Conclusion

Managing module dependencies in RequireJS is quite simple and becomes even simpler when leveraging the Sugar syntax described above. When doing so, it is important to keep in mind that this syntax relies on Function.prototype.toString(), which, while having good support in most modern browsers, does not provide predictable results in certain older browsers. However, as the documentation states, using an optimizer to normalize dependencies – such as the very powerful RequireJS Optimizer – will ensure this approach works across all browsers.

As a general rule of thumb, I typically use the Sugar syntax approach when there are more than 4-5 dependencies and have found it has simplified managing dependencies in modules rather nicely.

Preprocessing Modules with RequireJS Optimizer

RequireJS provides an effective means of bundling an application for production deployment by way of the RequireJS Optimizer; which allows for optimizing, concatenating and minifying modules via Node or Rhino.

The Optimizer is quite powerful insofar that fine-grained control over various aspects of the optimization process can be implemented with relative ease. One such example is the ability to specify a callback function to be invoked for each module in the build prior to optimization. This essentially provides the necessary hooks needed to implement a preprocessor.

The onBuildWrite option

The optimize method of the RequireJS Optimizer accepts an onBuildWrite option which allows for a callback to be specified. The callback will be invoked prior to serialization of each module within the optimized bundle. Callbacks receive the name, path and contents of the module; and, are always expected to return a value.

For example, consider the following build configuration which demonstrates a basic onBuildWrite callback that simply logs the name of each module processed by the build to the console and, returns the module’s content unmodified.

Using the above configuration, when executed against a (contrived) example consisting of just a single module, “ModuleA”, in Node, it would output the following to the console:

If we were to print out the contents of the files we would see something like this:

With this in mind, a basic preprocessor can be implemented quite easily using the onBuildWrite option. Assume the main.js script has a token placeholder for the build version like so:

We can implement a simple preprocessor which replaces the #version token with a build date as follows:

The above onBuildWrite callback logs the original contents, replaces the #version token with the current date, logs the result and returns the processed content. The output of which would be similar to the following:

As can be seen in the above examples, implementing a basic preprocessor is rather simple to accomplish with the RequireJS Optimizer. The ability to do so allows for some interesting possibilities and is certainly something worth checking out if you are leveraging AMD via RequireJS.

You can fork an example implementation at requirejs-preprocessor-example.