CommonJS specification

The development of Node makes the content of NPM module library increasingly rich, and the generation of these NPMS also follow the CommonJs specification. NPM is now indispensable to front-end developers, so the CommonJs specification and require() loading mechanism are extremely important to developers.

CommonJs module definition is divided into three parts: module reference, module definition and module identifier

The module reference

const sum = require('sum')
Copy the code

The module definition

In the CommonJs specification, the require method is used to introduce modules. The context in a module provides an exports object and module.exports object for exporting variables or methods of the current module.

Module. exports exports:

module.exports = {
   a: 2
}
Copy the code

Exports module:

exports.a = 2
Copy the code

Exports, module.exports: The require method only loads the module.exports object, and the exports object we use when writing modules is actually a reference to module.exports

Module identifier

The module id is the argument passed in when require(). Identifiers represent package names, which can also start with.,.. Relative path or absolute path at the beginning.

Module implements

There are two types of modules in Node:

  • One kind isNodeBuilt-in modules, also known as core modules (http,fsEtc.)
  • One is user – defined module, also known as file module

When we introduce modules in the current context, we need to go through the following four steps:

  1. Cache loading
  2. Path analysis
  3. File location
  4. File compilation execution

Module cache loading

Node caches imported modules, whether core or file, to reduce the overhead of re-importing. Node caches compiled objects. Cache loading is the first priority, and core module cache loading takes precedence over file module cache loading.

Path analysis

const sum = require('sum')
const sum = require('.. /sum')
const sum = require('/sum')
Copy the code

The require method takes a module identifier as an argument, sum,.. /sum and /sum are module identifiers. Node does module lookups based on such identifiers.

Identifier classification:

  • Core modules,http .fs , path
  • In order to..The relative path module at the beginning
  • In order to/The absolute path module at the beginning
  • Custom modules, such as numerous NPM packages
Loading of core modules:

The core module is only loaded by cache. When the Node process starts, the core module is compiled into binary code and loaded into memory.

PS: If we want to load a module with the same identifier as the core module, we must load it with a different identifier or a different path.

Create a test.js file in the same directory as http.js.

const a = require('http')
console.log(a)
Copy the code

Create a new http.js file

module.exports = {
    a : 1
}
Copy the code

Print the following:It can be seen that the printed content ishttpModule contents. If we try to load a module with the same identifier as the core module, we still read the core module. Then how to load the module with the same identifier as the core module, you can use the relative path or modify the identifier. Modified to introduce relative path:

const a = require('./http')
console.log(a)
Copy the code

The effect is as follows:

Module in path form

With the and.. An identifier at the beginning, when introduced, converts a relative path to an absolute path, and uses the absolute path as an index to add an object to the cache after the file has been compiled and executed, which is faster at secondary loading.

The loading of the file module indicates the location of the file, so it is fast in the lookup process, slower than the loading of the core module.

Custom modules

A custom module is usually a developer-wrapped module, such as the rich NPM community. Node loads a custom module following a set of path rules, which are represented as an array of paths. For the detailed rules of arrays, we can test them ourselves by creating a test file, find_path.js

console.log(module.paths)
Copy the code

In a Mac system, the output path array is as follows

[
  '/Users/zjg/study/demo/node_modules'.'/Users/zjg/study/node_modules'.'/Users/zjg/node_modules'.'/Users/node_modules'.'/node_modules'
]
Copy the code

As you can see, the rules for module pathfinding are as follows:

  1. To find files in the current directorynode_modulesdirectory
  2. In the parent directorynode_modulesdirectory
  3. Parent directory In the parent directorynode_modulesdirectory
  4. .
  5. Root directorynode_modulesdirectory

This way of querying a directory is very similar to JavaScript’s prototype chain or scope query.

File location

When we use require(), if the identifier does not include a file extension, for example

require('mytest')
Copy the code

In this case, Node will try to load modules by adding extensions in the order of.js,.json, and.node.

If mytest is an NPM package file, Node does not find the corresponding file by adding the file extension and instead finds a directory, Node treats the directory as a package.

Node has some support for the CommpnJs package specification. First, Node finds the packsge.json file in the current package directory, parses the package description object through json.parse (), and finds the entry file location specified by the main attribute. If no file extension is available, extension analysis is performed first. If the file specified by the main attribute does not exist, or there is no package.josn file in the current package directory, Node will try to find the index file name, including index.js, index.josn, and inndex.node.

If a module is not found in the current directory, Node starts looking in the next directory in the module path array. If no module is found, an error is thrown. That’s all Node does when loading a module.