We’ve using some sort of modules before with Coffeescript/Angular in Rails projects. Often we just had an object
App defined on the global scope, to which we attached the given module:
which compiles to
With the rails sprockets bundling, the different js files were compiled in the order specified in the manifest. There was not a way to control dependencies or load part of it asynchronously. The compiled coffee files were wrapped in a IIFE to provide isolation.
Our process has been very similar with angular
This is the classic pattern:
He goes to show a common template:
Variation: Import mixins
This shows how to pass and alias globals (e.g jQuery, Underscore) to our module:
Revealing Module pattern.
We define all of our functions and variables in the private scope and return an anonymous object with exposes the ones meant to be public: public.
Globals, CommonJS, AMD, UMD
With the previous patterns, we rely on a global variable that sets the namespace for that given module. This is known as the Globals option. In the last years, there’s been some approaches developed to account for dependency order and avoid namespace collisions, by implementing a convention that avoids going through the global scope.
A CommonJS module looks like this:
module is an special object that will keep a reference to the exported content provided in
myModule.js in a way in which it can be required be calling
require('myModule') in another file. This works on the server, where those requires will load those files synchronously, this is the Node.js approach. The reference passed to
module.exports doesn’t have to be a function:
AMD = Asynchronous Module Definition
This is the asynchronous variant for the browser. A couple of examples:
define method can receive three parameters
define(id?, dependencies?, factory);. The first is optional, it would be the module id, if it’s not present it would be the one that the loader (in this case
RequireJS) is expecting according to his configuration for that given file, for example:
The second argument is the
dependencies, an array of the module (ids) required by the module that is being defined. The dependencies must be resolved prior to the execution of the module
factory function, which is the third parameter, and the resolved values will be passed as arguments to this factory function with an order that matches those dependencies.
UMD (Universal Module Definition)
For projects that require you to support both AMD and CommonJS features, there’s yet another format: Universal Module Definition (UMD). UMD essentially creates a way to use either of the two, while also supporting the global variable definition. As a result, UMD modules are capable of working on both client and server.
Here there are some patterns to achieve it https://github.com/umdjs/umd
This is an example for the Rmodal library, that supports CommonJS, AMD or globals.
With the new ECMAScript edition, we have native support for a new type of modules. An ES6 module is a file containing JS code. That file is automatically in strict mode, and you can use the keywords
export inside it.
In that file everything you declare is local to the module, we use
export to make something public.
You can also rename both your exports and imports
To play nice with CommonJS and AMD modules, ES6 treats them as if they had a
Module objects: you can get the imports as properties from a module namespace object:
Some more good notes from * Es6 In-Depth Modules:
All exported identifiers must be explicitly exported by name in the source code. You can’t programmatically loop through an array and export a bunch of names in a data-driven way.
Module objects are frozen. There is no way to hack a new feature into a module object, polyfill style.
There is no error recovery for import errors. An app may have hundreds of modules in it, and if anything fails to load or link, nothing runs. You can’t import in a try/catch block. (The upside here is that because the system is so static, webpack can detect those errors for you at compile time.)