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

  1. exportSpecifies the external interface of a module
  2. importUsed 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

  1. A file is a module, and variables within the file are scoped within the module
  2. usemodule.exportsExternal interface of the object export module
  3. userequireIntroduce 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

  1. open--experimental-modules 
  2. 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…