Talking about the Module System in node. js

  • 2021-08-09 06:59:40
  • OfStack

Modules for Node. js

JavaScript comes out as a simple script language for adding interactive functions to web pages, but it does not contain module system when it was born. With JavaScript solving problems becoming more and more complex, all codes are written in one file, and function distinguishing functional units can no longer support complex application development. ES6 brings class and module, which are available in most high-level languages, which is convenient for developers to organize codes


import _ from 'lodash';

class Fun {}

export default Fun;

The above three lines of code show the two most important elements of a modular system, import and export
1. export Used to specify the external interface of the module
2. import Used to enter functions provided by other modules

Before ES6, there were many module loading schemes in the community, the most important of which were CommonJS and AMD. Node. js was born earlier than ES6, and the module system used an implementation similar to CommonJS and followed several principles

1. A file is a module, and the scope of variables in the file is within the module
STEP 2 Use module.exports External interface of object export module
STEP 3 Use require Introduce other modules

circle.js


const { PI } = Math;

module.exports = function area(r) {
 PI * r ** 2;
};

The above code realizes one module of Node. js, and the module does not depend on other modules. The method area is derived to calculate the area of a circle

test.js


const area = require('./circle.js');
console.log(` The radius is  4  The area of the circle is  ${area(4)}`);

The module relies on circle. js to calculate the area of the circle using its externally exposed area method

module.exports

The module exposes the interface to the outside using module. exports, which is commonly used in two ways: adding attributes to it or assigning values to new objects

test.js


//  Add Attributes 
module.exports.prop1 = xxx;
module.exports.funA = xxx;
module.exports.funB = xxx;

//  Assign a value to a brand new object 
module.exports = {
 prop1,
 funA,
 funB,
};

The two writing methods are equivalent, and there is no difference when using them


const mod = require('./test.js');

console.log(mod.prop1);
console.log(mod.funA());

There is another direct use exports Object, but you can only add properties to it, and you can't assign values to new objects, which will be explained later


//  Correct writing: Add attributes 
exports.prop1 = xxx;
exports.funA = xxx;
exports.funB = xxx;

//  Assign a value to a brand new object 
module.exports = {
 prop1,
	funA,
 funB,
};

require('id')

Module type

require is simple to use, and id supports two types: module name and file path

Module name


const fs = require('fs');
const _ = require('lodash');

In the example, fs and lodash are all module names, fs is the core module built in Node. js, and lodash is the third module installed under node_modules through npm. In case of duplicate names, the system built-in module is preferred

Because one project may contain multiple node_modules folders (Node. js comparison failed design), the third-party module search process will follow the proximity principle layer by layer (module. paths can be printed in the program to see the specific search path) until the file system root directory is found according to the NODE_PATH environment variable. For the specific process, please refer to the official document

In addition, Node. js searches the following list of global directories:

$HOME/.node_modules $HOME/.node_libraries $PREFIX/lib/node

Where $HOME is the user's home directory and $PREFIX is 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

File path

Modules can also be loaded using file path, which is a general loading method for custom modules in the project. The extension name can be omitted from the path, and will be tried in the order of. js,. json and. node

The module prefixed with '/' is the absolute path of the file. Find the module according to the system path Modules prefixed with './' are relative to the file that is currently calling the require method and are not affected by where subsequent modules are used

Single load & Cyclic dependency

The module is cached after the first load to the Module._cache If every time you call require('foo') Are resolved to the same 1 file, the same object is returned, and multiple calls are made at the same time require(foo) Does not cause the code of the module to be executed multiple times. Node. js caches modules based on the actual filename, so referencing the same module from different hierarchical directories does not repeat the load.

Understanding the module single load mechanism is convenient for us to understand the phenomenon after module cyclic dependence

a.js


console.log('a  Begin ');
exports.done = false;
const b = require('./b.js');
console.log(' In  a  In, b.done = %j', b.done);
exports.done = true;
console.log('a  End ');

b.js


console.log('b  Begin ');
exports.done = false;
const a = require('./a.js');
console.log(' In  b  In, a.done = %j', a.done);
exports.done = true;
console.log('b  End ');

main.js


console.log('main  Begin ');
const a = require('./a.js');
const b = require('./b.js');
console.log(' In  main  In, a.done=%j , b.done=%j', a.done, b.done);

When main. js loads a. js and a. js loads b. js, b. js attempts to load a. js

To prevent an infinite loop from returning an unfinished copy of the exports object of a. js to the b. js module, then b. js completes loading and supplies the exports object to the a. js module

So the output of the example is

main begins
a Start
b begins
In b, a. done = false
End of b
In a, b. done = true
End of a
In main, a. done = true, b. done = true

It doesn't matter if you don't understand the above process. You don't need it at all in your daily work. Even if you understand it, don't use circular dependency in your project!

Working principle

Node. js Each file is a module, and the variables in the module are local variables, which will not pollute the global variables. Before executing the module code, Node. js will use the following function wrapper to encapsulate the module


const { PI } = Math;

module.exports = function area(r) {
 PI * r ** 2;
};
0 __filename: Absolute path to the current module file __dirname: Absolute path to the directory where the current module file is located module: Current module instance require: Method for loading other modules, shortcut to module. require exports: Object to export module interface, shortcut to module. exports

Looking back at the initial question, why doesn't exports object support assigning values to other objects? It is very simple to add a sentence exports object source to the above function


const { PI } = Math;

module.exports = function area(r) {
 PI * r ** 2;
};
1

Other modules require to the module module. exports object, if the exports object assigned to other objects, and module. exports object disconnected, naturally useless

Using ES Module in Node. js

As ES6 becomes more widely used, Node. js also supports ES6 Module in several ways

babel Construction

Building with babel is the simplest and most common way before v12. For specific configuration, refer to @ babel/preset-env

.babelrc


const { PI } = Math;

module.exports = function area(r) {
 PI * r ** 2;
};
2

Native support

ES Module can be supported in native mode after v12

Open --experimental-modules The module name is changed to .mjs (Strongly not recommended) or package. json import0

In this way, Node. js will treat js files as ES Module. Please refer to the official documents for more details

The above is to talk about node. js module system details, more information about node. js module please pay attention to other related articles on this site!


Related articles: