1. Lack of JS modularity
For JS itself, its specification is weak, with the following deficiencies:
- No modular system, no support for closed scope and dependency management
- No standard library, no file system, no IO stream API
- There is no package management system
2 CommonJS functions
- Encapsulated functionality
- Closed scope
- May resolve dependency issues
- Work efficiency is higher, refactoring is convenient
3. CommonJS module specification
CommonJS is a widely used JavaScript modularity specification. The core idea is to synchronously load dependent modules via require method and export exposed interfaces via module.exports.
3.1 Module Reference
In the CommonJS specification, there is the require() method, which takes the module identity and thus introduces a module’s API into the current context. The example code for the module reference is as follows:
const path = require("path");
Copy the code
3.2 Module Definition
The context provides an exports object that exports the methods or variables of the current module, and it is the only export exported.
Within a module, there is also a Module object that represents the module itself, and exports are attributes of the Module.
In Node, a file is a module. You can define how to export a method by mounting it on an exports object.
// math.js
exports.add = function(){
var sum = 0,
i = 0,
args = arguments,
l = args.length;
while(i < l){
sum += args[i++]
}
return sum;
}
Copy the code
In another file, we can invoke the defined properties or methods after we introduce the module with the require() method:
var math = require("math");
exports.increment = function(val){
return math.add(val, 1)}Copy the code
3.3 Module Identifier
The module identifier is simply an argument passed to require(). It must be a string named after a small hump, or a string that starts with.,.. Relative path to the beginning, or absolute path.
CommonJS builds a modular export and import mechanism that allows the user to ignore variable pollution completely, and namespaces and other solutions are dwarfed by this.
4 Node module implementation
4.1 Steps for Importing a Module into a Node
- (1) Path analysis
- (2) File location
- (3) Compile and execute
4.2 Module Classification
4.2.1 Native Modules
Modules such as HTTP, FS, PATH and Events are provided by Node. These modules are compiled into binary during the compilation of Node source code. When the Node process starts, some of the native code is loaded directly into memory, so when the native module is introduced, the file location and compilation steps can be omitted, and the path analysis is the first to determine, so the loading speed is the fastest. Native modules are loaded by name.
4.2.2 File module
The process of path analysis, file location, compilation and execution is slower than that of the native module.
File modules are loaded by name or path. There are three suffixes for file modules, as follows
- .js — needs to be read into memory before running
- Json — fs reads it into memory and converts it into a JSON object
- .node — compiled binary C/C++ extension module file that can be used directly
4.2.3 Third-party Modules
- if
require
A function that specifies only a name is treated as a slavenode_modules
Load the file below, so you can move the module without changing the referenced module path - The query paths of third-party modules are as follows
module.paths
And the global directory - Load the slowest
Global directory
Window If NODE_PATH is set to a valid disk directory in the environment variable, require will look for the module in that directory if the module cannot be found locally.
On UNIX, $HOME/.node_modules $HOME/.node_libraries
4.3 Loading Policy
4.3.1 Load from the cache first
Node caches all imported modules to reduce the cost of secondary introduction. Unlike front-end browsers that cache static scripts, browsers only cache files, while Node caches compiled and executed objects.
The require() method takes a cache-first approach to loading the same module, whether it is a native module or a file module, which is the first priority.
Cache priority policy, as shown below:
4.3.2 Path Analysis and File Location
module.paths
The module path
console.log(module.paths)
[ '/ Users / * * / Documents/framework/article/node in the CommonJS/node_modules'.'/Users/****/Documents/framework/article/node_modules'.'/Users/**/Documents/framework/node_modules'.'/Users/**/Documents/node_modules'.'/Users/**/node_modules'.'/Users/node_modules'.'/node_modules' ]
Copy the code
During loading, Node tries each path in module.paths until it finds the target file. Therefore, the current file path is about deep, and the module search takes more time. Therefore, third-party modules are the slowest to load.
File location
- (1) File name extension order:.js >.node >.json
The attempt requires a synchronous block call to the FS module to determine whether the file exists, which can cause performance problems because it is single-threaded.
Here’s the trick: If it’s a. Node or. Json file, pass it with the extension.
- (2) Directory analysis and packages
require()
After analyzing the file name extension, it may not find the corresponding file, but it does find a directory, which Node will treat as a package.
First, Node looks for package.json in the current directory and locates the file specified by the main property. If the file lacks an extension, it will go to the extension analysis step. If the main property specifies the wrong file name, or if there is no package.json at all, Node takes index as the default file name and looks for index.js, index.json, and index.node in turn.
If no file is found in the directory analysis, go to the next module path for searching. If the array of module paths has been traversed and no object file has been found, an exception of failed to find will be thrown.
4.3.3 Summary of file module search rules
The diagram below:
5 Module compilation (file module)
5.1 module
The properties of the
In Node, each file module is an object, defined as follows:
console.log(module)
/* Module { id: '.', exports: {}, parent: null, filename: '/Users/... The CommonJS / / article / 015 _node tempCodeRunnerFile. Js', the loaded: false, children: [], paths: ['/Users/.../article/015_node CommonJS/node_modules', '/Users/.../article/node_modules', '/Users/.../node_modules', '/Users/.../node_modules', '/Users/.../node_modules', '/Users/node_modules', '/node_modules' ] } */
Copy the code
Compilation and execution are the last stages in the introduction of a file module. Once the file is located, Node creates a module object, which is loaded and compiled according to the path. For different file extensions, the loading method is also different, as shown below:
- .js file. The fs module synchronously reads the files and compiles them for execution.
- The node file. This is an extension file written in **C/C++, and the final compiled file is loaded through the dlopen()** method.
- The json file. throughfsAfter the module reads the file synchronously, use
JSON.parse()
Parse returns the result. - The rest of the extension files. They are all loaded as **.js** files
5.2 Compilation of JS module
During compilation, Node wraps the contents of the JS file from the top to the bottom, so that each file module is scoped. As follows:
(function(exports, require, module, __filename, __dirname){})Copy the code
The principle of the require method is simulated as follows:
// b.js
console.log('b.js')
exports.name = "b"
// a.js
let fs = require('fs');
let path = require('path')
let b = require2('./b.js')
function require2(mod) {
let filename = path.join(__dirname, mod);
let content = fs.readFileSync(filename, 'utf8');
let fn = new Function('exports'.'require'.'module'.'__filename'.'__dirname', content + "\n return module.exports")
let module = {
exports: {}}return fn(module.exports, require2, module, __filename, __dirname)
}
// b.js
Copy the code
6 exports
VS module.exports
Exports and module.exports are both publicly accessible, but there are differences.
6.1 contact
Exports are only an address reference for module.exports.
Nodejs will only export module.exports. If exports are changed, then exports will no longer be exported.
Here’s an example:
// test3.js
let counter = 0;
exports.printNextCount = function () {
counter += 2;
console.log(counter);
}
module.exports = function () {
counter += 10;
this.printNextCount = function () {
console.log(counter)
}
}
console.log(exports);
console.log(module.exports);
console.log(exports === module.exports);
/* { printNextCount: [Function] } [Function] false */
// test3_require.js
let Counter = require('./test3.js')
let counterObj = new Counter();
counterObj.printNextCount();
/ * 10 * /
Copy the code
6.2 the difference between
6.2.1 Fundamental Differences
- Exports return module functions
- Module. exports returns the module object itself and returns a class
Here’s an example:
// test1.js
let counter = 0;
exports.temp = function () {
counter += 10;
this.printNextCount = function () {
console.log(counter); }}console.log(exports);
console.log(module.exports);
console.log(exports === module.exports);
/* {temp: [Function]} /* {temp: [Function]} /* {temp: [Function]
// test1_require.js
// Can only be called as a function
let counter = require('./test1')
console.log(counter) // { temp: [Function] }
counter.temp() // Can only be called as a function
Copy the code
6.2.2 Usage Differences
- Exports method can be called directly
- Module. exports requires a new object to be called
The benefit of using this is that exports can only expose a single function, whereas module.exports can expose a class
Here’s an example:
// test2.js
let counter = 0;
module.exports = function () {
counter += 10;
this.printNextCount = function () {
console.log(counter); }}console.log(exports);
console.log(module.exports);
console.log(exports === module.exports);
/* {} [Function] // this is a class that requires new to call false
// test2_require.js
let Counter = require('./test2');
// An error is reported for direct invocation
// console.log(Counter.printNextCount()) // TypeError: Counter.printNextCount is not a function
// new an object to call again
let counterObj = new Counter();
counterObj.printNextCount();
/ * 10 * /
Copy the code
6.3 Usage Suggestions
- It’s best not to define them separately
module.exports
andexports
- Export object
module.exports
To export multiple methods and variablesexports
7 References
- Node.js is simple
- Exports and exports in nodeJS
- Exports: exports: exports