This document is for reading notes.
Module specification of COMMONJS
Node with the browser and the Organization, CommonJS Organization, ECMAScript the relationship between
Node Reference CommonJS of the Modules Specification realize a set of module system, so first look at COMMONJS module specification.
The COMMONJS definition of the module is very simple, mainly divided into module reference, module definition and module identification 3 parts.
1. Module reference
The sample code for the module reference is as follows:
var math = require (' math ');
In the COMMONJS specification, there is the require () method, which accepts the module identifier as a way to introduce a module's API into the current context.
2. Module definition
In the module, the context provides the require () method to introduce an external module. Corresponding to the introduced functionality, the context provides a method or variable that the exports object uses to export the current module, and it is the only exported export. In the module, there is also a Module object, which represents the module itself, and exports is the property of the modules. In node, a file is a module that mounts the method as a property on the exports object to define how the export will be exported:
Math.js
Exports.add = function () {
var sum = 0, i = 0, args = arguments, L = args.length;
while (I < L) {sum + = args[i++]; }
return sum;
};
In another file, after we introduce the module through the Require () method, we can invoke the defined property or method:
Program.js
var math = require (' math ');
Exports.increment = function (val) {return Math.add (Val, 1);};
3. Module identification
The module identifier is actually a parameter passed to the Require () method, which must be a string that conforms to the small hump name, or a. 、.. The relative path to the beginning, or the absolute path. It can have no filename suffix. js. The definition of the module is very simple and the interface is very concise. Its significance is to limit the clustering methods and variables to the private scope, while supporting the introduction and export capabilities to seamlessly connect upstream and downstream dependencies. Each module has a separate space, and they do not interfere with each other, and also appear crisp when referenced.
node in the implementation of the module node is not fully implemented in accordance with the specification, but the module specification has a certain trade-offs, but also added a little bit of their own needs of the characteristics. Although the exports, require, and module in the specification sound simple, the process needs to be known about what node is going through in the process of implementing them.
The introduction of modules in node requires the following 3 steps.
1. Path analysis
2. Document positioning
3. Compile execution
In node, the modules are divided into two categories: one is node-provided modules, called core modules, and the other is a user-written module called a file module.
? The core module is compiled into a binary execution file during the compilation of node source code. When the node process starts, some of the core modules are loaded directly into memory, so when this part of the core module is introduced, the two steps of file location and compile execution can be omitted and prioritized in the path analysis, so it loads faster.
? The file module is dynamically loaded at runtime and requires a complete path analysis, file location, and compilation execution, which is slower than the core module.
1. Priority loading from cache
Like the front-end browser caches static script files to improve performance, node caches the introduced modules to reduce the overhead of two ingestion. The difference is that the browser caches only the files, and node caches the objects after compilation and execution. whether it is the core module or the file module, the Require () method uses cache precedence for two loads of the same module, which is the first priority. The difference is that the cache check of the core module precedes the cache check of the file module.
2. Path analysis and file positioning
Because there are several forms of identifiers, there are varying degrees of differences in the search and positioning of the modules for different identifiers.
1. Module identifier analysis
node is based on a module identifier for module lookups. module identifiers are mainly categorized into the following categories in node.
- Core modules, such as HTTP, FS, path, and so on.
- . Or.. The starting relative path file module.
- The absolute path file module with/begins.
- Non-path form of a file module, such as a custom connect module.
? Core modules
The priority of the core module is second only to the cache load, which has been compiled into binary code during node's source code compilation, with the fastest loading process. If you attempt to load a custom module that is the same as the core module identifier, it will not succeed. If you have written an HTTP user module and want to load successfully, you must choose a different identifier or how to swap the path.
? File modules in path form
To... and/or start identifiers, which are treated as file modules. When parsing a path module, the Require () method converts the path to the real path and indexes the actual path, storing the results of the compiled execution in the cache, so that two times the load is faster. Because the file module specifies the exact file location for node, it can save a lot of time during the lookup process, which is slower than the core module.
? Custom Modules
A custom module refers to a non-core module and is not an identifier in the form of a path. It is a special kind of file module, it may be a file or package form. The search for such modules is the most time-consuming and the slowest of all methods.
2. Document positioning
The optimization strategy loaded from the cache makes the process of two ingestion without path analysis, file location and compile execution, which greatly improves the efficiency of loading the module again. However, there are some details to be noted during the document positioning process, including the parsing of file extensions, the processing of directories and packages.
? File extension Analysis
The COMMONJS module specification also allows a file extension to be included in the identifier, in which case node will make up the extension in the order of. JS,. JSON,. node, and then try again. During the attempt, the FS module needs to be called synchronously to determine if the file exists. Because node is single-threaded, this is a place that can cause performance problems. The trick is: if you have a. node and. json file, a little speed can be speeded up with the extension in the identifier passed to require ().
? Directory Analysis and Packages
During the parsing of identifiers, require () may not find the corresponding file after parsing the file name extension, but it will get a directory where node handles the directory as a package.
In this process, node has a certain degree of support for the COMMONJS package specification. First, node looks in the current directory for Package.json (package description file COMMONJS Package specification definition), resolves the package description object by Json.parse (), and takes the filename specified by the main property to locate. If the file name is missing an extension, the extension parsing step is entered. If the main property specifies an incorrect file name, or if there is no Package.json file at all, node will use index as the default file name, and then look for Index.js, Index.node, Index.json.
If no files are successfully located during directory analysis, the custom module goes to the next module path to find it. If the module path array is traversed and the target file is still not found, the lookup failed exception is thrown.
3. Module compilation
In node, each file module is an object, which is defined as follows:
function Module (ID, parent) { this.id = ID; This.exports = {}; This.parent = parent; if (parent && parent.children) { Parent.children.push (this); } This.filename = null; this.loaded = false;
Compilation and execution are the last stages of introducing a file module. When you navigate to a specific file, node creates a new module object and then loads and compiles it according to the path. For different file extensions, the loading method is different, as shown in the following.
? . js file.
The compilation executes after the file is read synchronously through the FS module.
? . node file.
This is an extension file written in C/s + +, which is loaded with the Dlopen () method to generate the last compiled file.
? . json file.
After the files are read synchronously through the FS module, the results are returned with Json.parse () resolution.
? The rest of the extension files.
They are all loaded as. js files.
Each successfully compiled module caches its file path as an index on the Module._cache object to increase the performance of the two introductions.
Compilation of JavaScript modules
Back to the COMMONJS module specification, we know that there are 3 variables in each module file, such as require, exports, module, but they are not defined in the module file, so where do they come from? Even in node's API documentation, we know that there are two variables, __filename and __dirname, in each module, and where do they come from? If we put the process of directly defining modules on the browser side, there will be a situation where the global variables are polluted.
In fact, during compilation, node wraps the contents of the JavaScript file that was acquired. Added in the header (function (exports, require, module, __filename, __dirname) {\ n, added \ n} at the tail); A normal JavaScript file will be packaged like this:
(function (exports, require, module, __filename, __dirname) {
var math = require (' math ');
Exports.area = function (RADIUS) {
return Math.PI * radius * RADIUS;
};
});
This allows for scope isolation between each module file. The code after the wrapper is executed by the VM native module's Runinthiscontext () method (similar to eval, which has a clear context, does not pollute the global), and returns a specific function object. Finally, the exports property of the current module object, the Require () method, the module (Modules object itself), and the full file path and file directory obtained in the file location are passed as parameters to the function () execution.
3. Package and NPM outside of the module, packages and NPM are a mechanism to link the modules together.
The definition of COMMONJS package specification is also very simple, it consists of package structure and package description file two parts, the former is used to organize the various files in the package, the latter is used to describe the package related information for external read analysis.
1. Package structure package is actually an archive file, that is, a directory directly packaged into a. zip or tar.gz format file, after the installation decompression to the directory. The package directories that fully conform to the COMMONJS specification should contain the following files.
- Package.json: Package description file.
- Bin: The directory used to store executable binaries.
- LIB: The directory used to store JavaScript code.
- Doc: The directory where the document is stored.
- Test: The code used to hold the unit test case.
2. Package Description Package Description file is used to express non-code related information, it is a JSON-formatted file--package.json, located at the root of the package, is an important part of the package. All of NPM's behavior is closely related to the fields of the package description file.
This can be seen in the NPM official website on Package.json definition specification.
You can upload your package to the NPM repository via NPM AddUser, NPM publish.
Third, off-topic: AMD, CMD, compatible with a variety of module specifications of the class library 1. AMD is an extension of the COMMONJS module specification, and its modules are defined as follows:
Define (ID?, dependencies, Factory);
2.CMD
3. Compatible in order to allow the same module to run on the front and back end, it is necessary to consider a compatible front end in the writing process to implement the module specification environment. In order to maintain the consistency of the front and back end, class library developers need to wrap the class library code inside a closure. The following code shows how the Hello () method can be defined in a different run environment, compatible with node, AMD, cmd, and common browser environments:
Related article: http://ifandelse.com/its-not-hard-making-your-library-support-amd-and-commonjs/
module mechanism in node. js