Node. Js module
JavaScript came into being as a simple scripting language to add interactive functions for web pages. At the beginning, it did not include module system. As JavaScript becomes more and more complex to solve problems, all codes are written in a file, and it is no longer possible to support complex application development by using function to distinguish functional units. ES6 brings classes and Modules that are common in most high-level languages to make it easier for developers to organize code
import _ from 'lodash';
class Fun {}
export default Fun;
Copy the code
The three lines above show the two most important elements of a modular system: import and export
export
Specifies the external interface of a moduleimport
Used to enter functionality provided by other modules
Node.js was born earlier than ES6, and the module system uses the implementation similar to CommonJS, following several principles
- A file is a module, and variables within the file are scoped within the module
- use
module.exports
External interface of the object export module - use
require
Introduce other modules
circle.js
const { PI } = Math;
module.exports = function area(r) {
PI * r ** 2;
};
Copy the code
The above code implements a node.js module, which does not rely on other modules, and exports the method area to calculate the area of the circle test.js
const area = require('./circle.js');
console.log(The area of a circle of radius 4 is zero${area(4)}`);
Copy the code
The module relies on circ. js to calculate the area of a circle using its exposed area method
module.exports
Modules use module.exports to expose interfaces and can be used in two common ways: to add attributes to them or to assign values to a new object, test.js
// Add attributes
module.exports.prop1 = xxx;
module.exports.funA = xxx;
module.exports.funB = xxx;
// Assign to a new object
module.exports = {
prop1,
funA,
funB,
};
Copy the code
They’re equivalent, so there’s no difference when you use them
const mod = require('./test.js');
console.log(mod.prop1);
console.log(mod.funA());
Copy the code
There is another way to use exports directly, but you can only add attributes to them, not assign them to new objects, for reasons explained below
// The correct way to write this is to add attributes
exports.prop1 = xxx;
exports.funA = xxx;
exports.funB = xxx;
// Assign to a new object
module.exports = {
prop1,
funA,
funB,
};
Copy the code
require(‘id’)
Module type
The use of require is relatively simple. Id supports two types: module name and file path
Module name
const fs = require('fs');
const _ = require('lodash');
Copy the code
Fs is the built-in core module of Node.js. Lodash is a third-party module installed in node_modules via NPM. It is preferred to use built-in modules because a project may contain multiple node_modules folders (node.js failed design). The search for third-party modules will follow the principle of going back layer by layer (you can print module.paths in the program to check the specific search path). Node.js searches for the following list of global directories until the file system root directory is found based on the NODE_PATH environment variable:
- $HOME/.node_modules
- $HOME/.node_libraries
- $PREFIX/lib/node
$HOME is the user’s HOME directory, and $PREFIX is the node_prefix configured in Node.js. It is strongly recommended that all dependencies be placed in the local node_modules directory, which will load faster and more reliably
The file path
Modules can also be loaded using the file path, which is the common way to load custom modules within a project. The path can omit the extension name, and will be tried in the order of.js,.json, and.node
- In order to
'/'
The module prefixed by is the absolute path of the file. Follow the system path to find the module - In order to
'/'
Is the prefixed module relative to the file that currently calls the require method, regardless of where the subsequent module is used
Single load & loop dependency
Modules are cached in module._cache after the first load. If every call to require(‘foo’) resolves to the same file, the same object is returned. Calling require(foo) multiple times at the same time will not cause the Module’s code to be executed multiple times. Node.js caches modules based on the actual file name, so referencing the same module from different levels of directory will not be loaded repeatedly. The understanding of module single loading mechanism is convenient for us to understand the phenomenon of module cyclic dependence
console.log('a beginning');
exports.done = false;
const b = require('./b.js');
console.log('在 a 中,b.done = %j', b.done);
exports.done = true;
console.log(End of the 'a');
Copy the code
b.js
console.log('b start');
exports.done = false;
const a = require('./a.js');
console.log('in b, a. tone = %j', a.done);
exports.done = true;
console.log(End of the 'b');
Copy the code
main.js
:
console.log('the main start');
const a = require('./a.js');
const b = require('./b.js');
console.log('In main, a.tone =%j, b.tone =%j', a.done, b.done);
Copy the code
When main.js loads a.js, a.js loads b.js, and b.js tries to load A.js
To prevent an infinite loop that would return an unfinished copy of the EXPORTS object of A. js to the B. js module, b.js then finished loading and provided the exports object to the A. js module
So the output of the example is
A. one= false B. One =true A. One =true B. One =true A. One =true A. One =true B. One =trueCopy the code
It doesn’t matter if you don’t understand the above process, you don’t need it in your daily work, and even if you do, don’t use circular dependencies in your projects!
The working principle of
Each node.js file is a module. Variables in a module are local variables and do not pollute global variables. Before executing the module code, Node.js uses a function wrapper to wrap the module
(function(exports.require.module, __filename, __dirname) {
// The module code is actually here
});
Copy the code
- __filename: absolute path to the current module file
- __dirname: absolute path to the directory where the current module file data resides
- Module: current module instance
- Require: a shortcut to loading other modules, module.require
- Exports: shortcuts to objects that export module interfaces. Module. exports
Going back to the original question, why does exports not support assignment to other objects? It would be easy to add an exports object source to the above function
const exports = module.exports;
(function(exports.require.module, __filename, __dirname) {
// The module code is actually here
});
Copy the code
Module. exports is required by any other module. If an exports object is assigned to another object, it will be disconnected from the module.exports object
Use ES Module in Node.js
As ES6 becomes more widely used, Node.js also supports ES6 Modules in several ways
Babel build
Building with Babel is the easiest and most common way to build before V12, see @babel/ preth-env.babelrc for configuration
{
"presets": [["@babel/preset-env", {
"targets": {
"node": "8.9.0"."esmodules": true}}}]]Copy the code
Native support
ES Module can be supported natively after V12
- open
--experimental-modules
- The module name is changed to
.mjs
(strongly not recommended) or package.json"type": module
Node.js treats js files as ES modules. See the official documentation for more details
The minimalist Node. Js introductory tutorial: www.yuque.com/sunluyong/n…
In this paper, a better reading experience: www.yuque.com/sunluyong/n…