When developing applications with a modular structure in JavaScript there are two problems:
- description and satisfaction of the dependencies of various parts of the application, the need to connect dependency on the server side;
- exporting variables into the global scope and their collision.
Both of these problems can be solved using the approach of Asynchronous Module Definition . This approach is reduced to the description of function modules define and connect them with the “require” method. At the moment, there are several tools that implement AMD. I began my acquaintance with them through RequireJS and was surprised how easy and simple it is to describe the module dependencies. I’ll tell you how it works by providing a simple example.
Connecting RequireJS Loader
We have the following directory structure:
siteroot/
js/
app.js
require.js
jquery.js
mymodule.js
index.html
To start, we need to connect to the index.html loader. We will use RequireJS: <script Data-main = “/ JS / app” src = “/ JS / require.js”> </ script>. This is the only tag script, which we need at this time. The rest of the work of connecting the JS scripts is done by a boot loader. Specified in the data-attribute file extension (extension .js for short RequireJS always omitted) is a kind of entry point of the application. In it we will be able to connect the necessary modules using require and carry out its plan of action.
Module Description
We describe our module in “/js/module.js” using define function:
define(
'mymodule',
['jquery'],
function( $ ){
return {
foo : 'bar'
};
}
);
The first argument is a string, the name of the module is not required. The second argument passed according to a string array is also optional. The third argument is a factory function, which is performed only after the satisfaction of all the dependencies (download of these files). In its function the arguments are passed as export of the dependent variables. And it should return the module itself. In this case, an object with a single field.
Module Usage
In /js/app.js connect the required modules using JS and execute its code:
require(
['mymodule', 'jquery'],
function( Module, $ ){
$('body').append( Module.foo );
}
);
Module is not to be available in the global scope, as well as other variables that are exported from the library dependencies. Despite the fact that the jQuery library version 1.7 supports AMD-architecture, it is an exception: it exports its dollar into the global visible scope. Most likely, this is done to maintain compatibility with an army of plug-ins written for many years.
Configuration
RequireJS has a number of parameters that can be transmitted before use. For this purpose we use require.config object. What to do if you need to connect the module that is not issued in the form of AMD and exports a variable in the global scope? You can, of course, modify the source code, but it is bad practice. For descriptions of these modules we use setting shim. You can manually specify the dependencies and exported variables, and it will become part of our application along with other AMD-items:
require.config = {
shim: {
'oldmodule' : {
deps: [],
exports: 'OldModule'
}
}
};
Now you can specify it as a dependency:
require(
['mymodule', 'jquery', 'oldmodule'],
function(){}
);
In addition to the shim there are still many other parameters: the root directory file connection “baseUrl”, aliases for easier connection “paths”, etc.
Conclusion
I hope you found the concept of AMD interesting as it became for me. It helps to avoid chaos when a large number of JS-files in the design are connected which results in writing reusable code that takes responsibility for connection of the files from a backend. And if you have a really great MVC-application of a couple of dozen files, then such a system is simply irreplaceable.
Here are a few links that will help to further explore AMD & RequireJS:
- Addy Osmani Article Writing Modular JavaScript With AMD, CommonJS & ES Harmony ;
- official site RequireJS ;
- specification of AMD .
Happy hacking!
Comment Here