
When developing applications with a modular structure of JavaScript, two problems arise:
- description and satisfaction of dependencies of different parts of the application, the need to organize dependencies on the server side;
- export of variables to the global scope and their collision.
Both of these problems are solved using the
Asynchronous Module Definition approach. It comes down to the description of modules by the function define and connecting them with require. At the moment there are several tools that implement AMD. I began my acquaintance with them with
RequireJS and was surprised how convenient and easy it is to describe the dependencies of the modules. I'll tell you how it works, on a simple example.
Bootloader connection
We have the following directory structure:
siteroot/ js/ app.js require.js jquery.js mymodule.js index.html
To begin with, we will connect the loader in index.html. We will use RequireJS:
<script data-main="/js/app" src="/js/require.js"></script>
Great, this is the only script tag we need. The rest of the JS connection work will be done by the bootloader. The file specified in the data attribute (the .js extension is always omitted in RequireJS for short) will be a kind of entry point for our application. In it, we will be able to connect the necessary modules with the help of require and perform the conceived actions.
Module Description
We describe our module in
/js/module.js
with define:
define( 'mymodule', ['jquery'], function( $ ){ return { foo : 'bar' }; } );
The first argument is a string, the name of the module, is optional. The second argument passes the dependencies as an array of strings, also optionally. The third argument is a factory function that is executed only after all dependencies have been satisfied (loading the listed files). Exported variables are passed to it as arguments. And it should return the module itself. In this case, it is an object with one field.
Using
In
/js/app.js
connect the necessary modules with JS and execute our code:
require( ['mymodule', 'jquery'], function( Module, $ ){ $('body').append( Module.foo ); } );
Module will not be available in the global scope, as well as other variables exported by libraries from dependencies. Despite the fact that the jQuery library from version 1.7 supports the AMD architecture, it is an exception: it exports its dollar to the global scope. Most likely, this is done to preserve compatibility with an army of plug-ins written over many years.
')
Configuration
RequireJS has a number of parameters that can be passed before use. For this is the object
require.config
.
What if you need to connect a module that is not designed as an AMD and exports the variable to the global scope? You can, of course, modify its source code, but this is a bad practice. To describe such modules is the parameter
shim
. You can manually specify its dependencies and the exported variable, and it will become part of our application along with other AMD guys:
require.config = { shim: { 'oldmodule' : { deps: [], exports: 'OldModule' } } };
Now you can specify it as a dependency:
require( ['mymodule', 'jquery', 'oldmodule'], function(){} );
In addition to shim, there are many more parameters: the root directory for connecting the
baseUrl
files, aliases for more convenient
paths
, etc.
Conclusion
I hope the concept of AMD hooked you, as well as me. It helps to avoid chaos when using a large number of JS-files in development, pushes to write a reuse code, removes the responsibility for connecting files from the backend. And if you have a really large MVC application from a couple of dozen files, then such a system is simply indispensable.
In parting, here are some links that will help continue the study of the issue:
The source code from the article is available
in the GitHub repository .
Happy hacking!