With the advent of the Internet era, front-end technologies are being updated at a faster and faster pace. Initially, a few lines of code embedded in
What is modularity
Modules are an integral part of any large application architecture. A module is a block of code or file that implements a specific function. Modules allow us to clearly separate and organize the units of code in a project. In project development, by removing dependencies and loosely coupling the application can be made more maintainable. With modules, developers can more easily use other people’s code and load whatever functionality they want. Module development needs to follow certain specifications, otherwise it will be chaotic.
A module is a set of methods that implement a specific function. Simply putting different functions (and variables that record state) together is considered a module.
The above functions
func2() make up a module. When you use them, you can just call them directly. The disadvantages of this approach are obvious: it “pollutes” the global variables, it does not guarantee that there are no variable name conflicts with other modules, and there is no direct relationship between module members.
Object writing method
To solve the above drawback, you can write the module as an object, and all the module members are put inside this object.
The above functions
func2() are encapsulated in the
moduleA object, and when used, they call the properties of this object.
However, this way of writing exposes that all module members, internal state can be rewritten externally. For example, external code can directly change the value of the internal counter.
Immediately-Invoked Function Expression
Using Immediately-Invoked Function Expression (IIFE), you can achieve the goal of not exposing private members.
With the above writeup, external code cannot read the internal
If a module is large and must be divided into several parts, or if a module needs to inherit from another module, it is necessary to use the “augmentation mode” (augmentation).
The above code adds a new method
func3() to the
moduleA module, and then returns the new moduleA` module.
Loose augmentation mode
In a browser environment, the various parts of a module are usually fetched from the web, and sometimes it is impossible to know which part will be loaded first. If the previous section is written, the first part to be executed may load a non-existent empty object, and then “Loose augmentation mode” is used.
In contrast to the “augmentation mode”, the “Loose augmentation mode” means that the parameters of the “Immediately-Invoked Function” can be empty objects.
Enter global variables
Independence is an important feature of modules, and it is desirable that modules do not interact directly with other parts of the program internally. In order to call global variables inside a module, other variables must be explicitly entered into the module.
moduleA module above requires the use of the jQuery library and the YUI library, so the two libraries (actually two modules) are entered as parameters to
The module system of node.js is implemented in reference to the CommonJS specification. According to the CommonJS specification, a single file is a module. Each module is a separate scope, which means that variables defined inside that module cannot be read by other modules unless they are defined as properties of a global object. The best way to export module variables is to use the module.exports object.
The above code defines a function that bridges the communication between the outside of the module and the inside, using the
module.exports object. The module is loaded using the
require method, which reads a file, executes it, and returns the
module.exports object inside the file.
Once you have a server-side module, it is natural to want a client-side module. And ideally the two should be compatible, so that a module can run in both the server and the browser without modification. But here’s the problem.
The second line,
math.add(2, 3), runs after the first line,
require('math'), so it must wait for
math.js to finish loading. That is, if it takes a long time to load, the whole application will just stop there and wait. This is not a problem for the server side, because all modules are stored on the local hard disk and can be loaded simultaneously, and the waiting time is the read time of the hard disk. However, for the browser, this is a big problem, because the modules are placed on the server side, the waiting time depends on the speed of the network, it may take a long time to wait, the browser is in a “false death” state. Therefore, the browser side of the module, you can not use “synchronous loading”, only “asynchronous loading”. This is the background of the birth of the AMD specification.
AMD stands for “Asynchronous Module Definition”, which means “Asynchronous Module Definition”. It loads the module asynchronously, so that the loading of the module does not affect the execution of the statements that follow it. All statements that depend on the module are defined in a callback function, which will not run until the loading is complete.
AMD also uses the
require() statement to load modules, but unlike CommonJS, it requires two arguments.
The first parameter
module is an array whose members are the modules to be loaded; the second parameter
callback is the callback function after the successful loading. If you rewrite the previous code in AMD form, it would look like this.
Let’s start with an example.
Define the module: (moduleA.js)
Load the module and call.
Syntax of requireJS.
requireJS defines a function
define, which is a global variable used to define the module.
idis an optional parameter that defines the module’s identity, and if it is not provided, the script filename (minus the extension)
dependenciesis an array of module names that the current module depends on
factoryis a factory method where the module initializes the function or object to be executed. If a function, it should be executed only once. If it is an object, this object should be the output value of the module
Load the module on the page using the
require() function accepts two arguments.
- the first argument is an array indicating the dependent modules
- The second argument is a callback function that will be called when all the previously specified modules have been loaded successfully. The loaded modules are passed into the function as arguments, so that they can be used inside the callback function
require() function loads dependent functions asynchronously, so that the browser does not lose response. Also it specifies a callback function that will run only after all the previous modules have been loaded successfully, solving the dependency problem.
CMD (Common Module Definition) is a specification that specifies the basic writing format and basic interaction rules for modules. This specification was developed in China. AMD is dependency front-loading, CMD is on-demand loading.
The CMD specification was developed in China, just like AMD has requireJS, CMD has a browser implementation of SeaJS, which solves the same problem as requireJS, except in the way modules are defined and loaded (so to speak, run, parse). SeaJS solves the same problem as requireJS, except that it differs in the way modules are defined and the timing of module loading (running, parsing, if you will).
Sea.js promotes one module, one file, and follows a uniform writing style
Because the CMD specification promotes a file as a module, the file name is often used as the module id. CMD advocates proximity of dependencies, so dependencies are generally not written in the
factory is the constructor of the module. This constructor is executed to get the interface that the module provides to the outside. The
factory method is executed with three parameters by default:
requireis the first parameter of the
factoryfunction, a method that accepts the module identifier as the only parameter to get the interfaces provided by other modules.
exportsis an object that is used to provide the module interface to the outside.
moduleis an object that stores some properties and methods associated with the current module.
The following is an example of a CMD-compliant specification.
AMD vs CMD Differences
The two specifications were originally developed for different purposes.
AMD is the output of the specification of module definitions made by RequireJS during the rollout CMD is the output of the specification of module definitions made by SeaJS during the rollout
For dependent modules, AMD preloads and CMD defers loading.
AMD promotes dependency preloading, declaring the dependent modules when defining the module CMD promotes proximity dependency, loading a module only when it is used
This difference has its advantages and disadvantages, it’s just a syntax difference, and both requireJS and SeaJS support each other’s writing style.
The biggest difference between AMD and CMD is that the timing of execution of dependent modules is handled differently, note that it is not the timing or manner of loading that is different.
Many people say that requireJS loads modules asynchronously and SeaJS loads them synchronously, but this understanding is actually inaccurate. In fact, both specifications load dependent modules asynchronously, except that AMD is “dependency-front”, where the factory can easily know what dependent modules are available, and CMD is “dependency-near”. You need to parse the module into a string when you use it to know which modules depend on it. This is one of the things that many people criticize about CMD, sacrificing performance to bring convenience to development, when in fact parsing modules takes so little time that it can be ignored.
Again, the modules are loaded asynchronously. AMD will execute the module after loading the module dependencies, and after all modules are loaded and executed it will enter the
require callback function and execute the main logic. The effect of this is that the order of execution of the dependencies and the order in which they are written are not necessarily the same, depending on the speed at which the modules are loaded and which one is executed first, but the main logic must be executed after all the dependencies have been loaded.
CMD does not execute a dependency module after it is loaded, it just downloads it. After all the dependency modules are loaded, it enters the main logic and executes the corresponding module only when it encounters the
require statement, so that the execution order of the modules and the writing order are exactly the same.
This is the reason why many people say that AMD has a good user experience because there is no delay and the dependent modules are executed in advance, and CMD has a good performance because it is executed only when the user needs it.
Future-proofing the ES6 modularity standard
Since the call for modular development is so high, the official ECMA must do something about it. In fact, ECMA has included modularity in the draft for a long time, and finally the standard specification for modularity was included in the ES6 official release in June 2015. However, probably due to the immaturity of the technology involved, ES6 removes the content about how modules are loaded/executed and only retains the syntax for defining and introducing modules, which requires no special work to define a module, since the role of a module is to provide APIs to the public. The module can be defined with the
module keyword, and the API that the module needs to provide to the public can be exported with
import keyword to load external modules.
The module requested from the server.
Dynamic loading of a module.