AMD, CMD, CommonJS, ES6

A, AMD

AMD, Asynchronous Module Definition It is a specification for modular development on the browser side. It is not javascript native support, so using THE AMD specification for page development needs to use the corresponding library, namely RequireJS. AMD is actually the output of the scope of module definition of RequireJS in the process of popularization.

RequireJS addresses two main issues:

  • multiplejsWhen a file has dependencies, the dependent file must be loaded into the browser before the dependent file
  • jsThe browser blocks the render thread when loading, and the more files loaded, the longer the page is unresponsive

Usage: require requires a root to start the search dependency (like main in package.json), and data-main to specify the root.

<script src="script/require.js" data-main="script/app.js"></script>
Copy the code

This specifies that root is app.js, and only modules that are directly or indirectly dependent on app.js will be inserted into the HTML.

  • define()Function: The function used to define a module.args0: an array of module names to import,arg1: after the dependency is introducedcallback.callbackThe argument of phi is the thing introduced. If there are multiple dependencies, the parameters are passed in the order they were introduced.
define(['dependence_name'], (args) => {
// Args is a dependence_name
// ... Your fucking code ...
   return your_export;
});
Copy the code
  • require()Function: A function used to introduce a module.
require(['import_module_name'], (args) => {
// args is what is imported from import_module_name
// ... Your fucking code ...
});
Copy the code
  • require.configConfiguration:
    • baseUrlLoading:moduleThe root of the path
    • paths: used to map the module path below the root path that does not exist
    • shimes: to load theAMDSpecification of thejs

Second, the CMD

CMD, Common Module Definition, Common Module Definition CMD was created during the promotion of sea-.js. In the CMD specification, a module is a file.

define(function(require, exprots, module) {
 const fs = require('fs');  // Accept the module id as the only argument
 // exports, Module is similar to CommonJS
 exports.module = {
  props: 'value'
 };
});

seajs.use(['test.js'].function(test_exports) {
/ /...
});
Copy the code
null AMD CMD
How dependencies are handled when defining modules Dependency prefixes are preferred, which declare dependent modules at the time of definition Advocate proximity, requiring only when the module is used
Loading way async async
The way module is executed After the module is loaded, the module will be executed. After the module is loaded, all modules will enter the require callback function to execute the main logic. Dependencies may not be executed in the same order as they are written, but the main logic must be executed after all dependencies have been loaded (similar to promise.all). Once a dependency is loaded, it is not executed; it is just downloaded. The module is executed only when the require statement is encountered after all modules are loaded. Modules are executed in exactly the same order as they are written.

Third, CommonJS

The OFFICIAL W3C apis can only be Browser based, and CommonJS makes up for the lack of javascript.

NodeJS is the primary practitioner of the CommonJS specification. It has four important environment variables that support modular implementations: Module, exports, require, and Global. In practice, use module.exports(not recommended) to define the API for exports and use require to reference modules. CommonJS loads modules synchronously. On Server the module files are on the local disk, so it’s ok to load them very quickly, but in Browser it makes more sense to load them asynchronously for network reasons. CommonJS module definition is divided into three parts: module reference, module definition and module identification.

1. Module Reference:

const fs = require('fs');
Copy the code

Require’s execution steps:

  1. If it is a core module, such as FS, return directly to the module
  2. If it is a path, concatenate it into an absolute path, and read the cache require.cache before reading the file. (If there is no extension, thenjs => json => node(read as a binary plug-in module)
  3. The module after first loading will be inrequire.cache, so require multiple times, resulting in the same object (reference to the same object)
  4. When executing module code, the module is wrapped in the following mode so that it is scoped within the module.
(function (exports, require, module, __filename, __dirname) {
  // module codes
});
Copy the code
  1. The wrapped code is executed with the VM native module’s runInThisContext() method (similar to eval, but with explicit context that does not pollute the environment) and returns a function object. Finally, the current module objectexports,requireMethods,moduleAnd from the location of the fileFull file path(including file name) andFile directoryPass to this function execution.

2. Module Definition:

function fn() {}
exports.propName = fn;
module.exports = fn;
Copy the code

A Module object represents the module itself, and exports is a module property. Exports can be defined by mounting properties on exports, or by assigning values directly to module.exports (recommended).

3. Module Identification:

The module identifier is the argument passed to the require() method, which can be a relative or absolute path, or a string that conforms to the small camel name. NodeJS CommonJS implementation: Node module is divided into the core module provided by Node and user prepared file module.

Core modules are compiled into binary executables during compilation of Node source code. Some core modules are loaded into memory when Node is started, so when referencing core modules, file location and compilation execution steps can be omitted, and the path judgment is prioritized, so it is the fastest to load. The file module is dynamically loaded at runtime, which requires complete path analysis, file location, compilation and execution, and is slower than the core module. There are three steps to importing a module into NodeJS:

  1. Paths = [‘ node_modules in current directory ‘, ‘node_modules in parent directory’,… ‘Node_modules in parent directory’]

  2. File location: file extension analysis, directory and package processing.

    • File extension analysis:NodeWill follow the.js => .json => .nodeThe order of complementing extensions is tried in sequence. (In the process of trying to call the synchronous FS module to see if the file exists)
    • Directory and package handling: there may be no corresponding files, but corresponding directories exist. At this momentNodeWill look in this directorypackage.jsonAnd,JSON.parseOut of themain(Entry file) corresponding file. ifmainAttribute error or nonepackage.json, it willindexAs amain. If no file is successfully located, go to the next module path and repeat the above work, if the entiremodule.pathsIf the target file is not found after traversing, the search failure error is run.
  3. Compile execution: Each module file is an object in Node, and compile execution is the last stage of importing file modules. Once the file is located, Node creates a new module object, which it loads and compiles according to the path. Different file extensions are loaded differently:

    • .jsThrough:fsThe module synchronously reads the file and compiles it
    • .node: this isC++Prepared by extension file, throughdlopen()Load the file generated by the last compilation.
    • .jsonWith:.jsFile, for laterJSON.parseParsing returns the result. Other files: all pressjsIn the way of analysis.
null CommonJS ES6
keywords exports, require, module, __filename. __dirname import, export
The import const path = require(‘fs’); All attributes exported by a module must be imported import path from ‘path’; You can just introduce one
export module.exports = App; export default App;
Imported objects Arbitrarily modify the value of the copy Reference of a value cannot be modified arbitrarily
The import number Require can be required any number of times, except for the first time, and subsequent require can be taken from require.cache Import in the header only once
loading Run time loading Compile-time output interface

ES6 module

ES6 module has been relatively familiar, usage is not repeated, directly on the code:

import { prop } from 'app';   // Import prop from app
import { prop as newProp } from 'app';   // Perform the same function as above, but rename the imported prop to newProp

import App from 'App';   // Import App default
import * as App from 'App';  // Import all properties of the App into the App object

export const variable = 'value'; // Export a constant named variable
export {variable as newVar};   // Export variable as newVar, similar to the import renaming

export default variable = 'value';  // Export variable as default
export {variable as default};   // The same as above

export {variable} from 'module';  // Export variable from module, which cannot be accessed
export {variable as newVar} from 'module';  // The following is not clear
export {variable as newVar} from 'module';
export * from 'module';
Copy the code

Ps: the ES6 module imports variables (which should be more accurately called constants) with the following characteristics: the variables are promoted, as if wrapped in object.freeze (), and import/export can only be in the top-level scope

ES6 modules differ from CommonJS runtime loading in that import commands are statically parsed by the JavaScript engine and executed prior to the rest of the module (similar to function declarations prior to other statements), meaning that import imports are brought to the top of the file anywhere in the file.

ES6 modules automatically turn on strict mode, even if ‘use strict’ is not written; . When you run a module with an import declaration, the imported modules are first imported and loaded, and the contents of each module are then traversed depth-first based on dependencies. Skip modules that have already been executed to avoid dependency loops.

The standard says very little about what import does, and ES6 leaves the loading details of modules entirely up to the implementation. Generally speaking, when the JS engine runs a module, its behavior can be roughly summarized as the following four steps:

  1. Parsing: Engine parses module code, checks syntax, and so on.
  2. Load: Load all introduced modules recursively, depth first.
  3. Linking: Create a scope for each newly loaded module and bind declarations from the module (including those imported from other modules) into it. whenjsWhen the engine starts executing the code loaded in the module,importThe processing is over, sojsThe engine executes to a lineimportWe’re not gonna do anything about it. Imports are implemented statically, and by the time the code is executed, nothing is done.

Speaking of modules, let me mention the difference between modules and scripts (note that I’m only talking about the difference in Web browsers) :

module script
Usage (there are other implementations, too, which are not discussed here) <script src=”./source.js type=”module” /> <script src=”./source.js type=”text/javascript” />
download ① When <script> is encountered, defer is automatically applied.

② Download && parsing Module.

③ Recursively download the resources imported from module. The download phase is complete.
When <script> is encountered, document rendering is blocked by default and download is enabled.
Implement way ① After downloading, the resources imported in module will be recursively executed.

② Then execute the Module itself.

Ps: The inline Module is missing the steps to download the Module itself. The other steps are the same as the imported Module.
The default is to execute immediately after downloading

Reference links:

Front-end modular: CommonJS,AMD,CMD,ES6

ES6 module system

NodeJS