The running mechanism of node modules is simple to understand. Cover general process, skip the underlying system distinction.

  1. CommonJS standard
  2. Node module loading process
  3. summary

1. com monJS specification

CommonJS specification, including module reference, module definition, module identification, three parts

Module references: Modules use the require method to synchronously load dependent modules

Module definition: A file is a module in Node and provides methods or variables that export the current module from exports objects

Module id: The module id is passed to the require() method as a string named camelCase or as a file path.

1.1. CommonJS application in node module

There are two ways to export module content:

The contents of A. js are as follows,

Method 1: You can mount exported variables or functions to the properties of exports objects

// Each node.js file is a separate module
// Node wraps the contents of the retrieved Javascript file, passing in the following variables
console.log(exports, require.module, __filename, __dirname);
// You can mount exported variables or functions to the properties of exports objects,
exports.name = 'luoxiaobu';
exports.age = '18'Copy the code

Exports a variable object or function as a whole using the module.exports object

// Each node.js file is a separate module
// Node wraps the contents of the retrieved Javascript file, passing in the following variables
console.log(exports, require.module, __filename, __dirname);
let name = 'luoxiaobu';
let age = '18'
// Exports an entire variable object or function using the module.exports object.
module.exports = {name,age};Copy the code

How modules are referenced: By the source from which they are referenced

// The core module is introduced into Node's own module
let crypto = require('crypto')

// User-written modules are introduced
let aModule = require('./a.js')
// third party, other people implement published module (actually also other users write)
let proxy = require('http-proxy');Copy the code

2. Node module loading process

Each Node.js file is a separate module, and each module is represented by a Module object.
The node NativeModule
/ / the node NativeModule
function Module(id = ' ', parent) {
  this.id = id;
  this.path = path.dirname(id);
  this.exports = {};
  this.parent = parent;
  updateChildren(parent, this.false);
  this.filename = null;
  this.loaded = false;
  this.children = [];
}Copy the code

NativeModule
// Set up NativeModule.
function NativeModule(id) {
  this.filename = `${id}.js`;
  this.id = id;
  this.exports = {};
  this.module = undefined;
  this.exportKeys = undefined;
  this.loaded = false;
  this.loading = false;
  this.canBeRequiredByUsers = ! id.startsWith('internal/');
}Copy the code

2.1 Description of Node Module Loading

Loading process :(module._load loading function)

Reference source: node/lib/internal/modules/CJS/loader. Js
Code has been trimmed slightly
// Check the cache for the requested file.
// 1. If a module already exists in the cache: return its exports object.
// 2. If the module is native: call
// `NativeModule.prototype.compileForPublicLoader()` and return the exports.
// 3. Otherwise, create a new module for the file and save it to the cache.
// Then have it load the file contents before returning its exports
// object.
Module._load = function(request, parent, isMain) {
    let relResolveCacheIdentifier;
    if (parent) {
      debug('Module._load REQUEST %s parent: %s', request, parent.id); . }// Find the file location
    const filename = Module._resolveFilename(request, parent, isMain);
    // There is no need to perform the return cache again
    const cachedModule = Module._cache[filename];
    if(cachedModule ! = =undefined) {
      updateChildren(parent, cachedModule, true);
      if(! cachedModule.loaded)return getExportsForCircularRequire(cachedModule);
      return cachedModule.exports;
    }
    // Load the node native module, loadNativeModule
    // Mod.exports is returned if it exists and can be referenced by the user. (This includes compiling the Node module to create module objects and storing the module running results on module objects)
    const mod = loadNativeModule(filename, request);
    if (mod && mod.canBeRequiredByUsers) return mod.exports;
  
    // Create a module
    // Don't call updateChildren(), Module constructor already does.
    const module = new Module(filename, parent);
  
    if (isMain) {
      process.mainModule = module;
      module.id = '. ';
    }
    // Cache module
    Module._cache[filename] = module;
    if(parent ! = =undefined) {
      relativeResolveCache[relResolveCacheIdentifier] = filename;
    }
    // Load and execute the new module
    module.load(filename);  
      
    return module.exports;
  };Copy the code

Node caches compiled and executed objects
The same:
Both node modules and non-Node modules go through the process of having cached objects executed and returning cached objects
No executed cache object, creates module object, executes module, stores executed object, returns executed result exports
Different:
Different cache objects
Different modes are used to load module files

2.2 Node source Directory

General source code structure :(only annotated parts of interest)

We can see the node library directory, where:
Deps:Contains libraries that Node relies on, such as V8, libuv, zlib, etc.,
Lib:Contains functions and modules defined in javascript (which may be called via internalBinding to the c++ module, which is implemented in the directory SRC),
SRC:Includes the corresponding C++ implementation of the lib library, many of which are built in here


Can be confused, js and c++ call each other?
Node.js mainly includes these parts, Node Standard Library, Node Bindings, V8, Libuv, and the architecture diagram is as follows:

Node Bindings: is a bridge between JS and C++, which translates the C++ interface exposed by V8 engine into JS API

V8: A JavaScript engine that provides a JavaScript runtime environment

C++ module reference process

C++ and JS interactive reference article :(interested can know)
C++ and JS interaction
Develop customizable applications using the Google V8 engine

2.3 Node Module Classification

Reference: the node/lib/internal/bootstrap/loaders. Js
// This file creates the internal module & binding loaders used by built-in
// modules. In contrast, user land modules are loaded using
// lib/internal/modules/cjs/loader.js (CommonJS Modules) or
// lib/internal/modules/esm/* (ES Modules).
//
// This file is compiled and run by node.cc before bootstrap/node.js
// was called, therefore the loaders are bootstraped before we start to
// actually bootstrap Node.js. It creates the following objects:

Node. Cc to compile and run the node/lib/internal/bootstrap/loaders, js file, create the internal module, binding the built-in module USES the loader.
And user module loading operation rely on lib/internal/modules/CJS/loader. The js or lib/internal/modules/esm / *
So Node modules fall into two categories:
  • The core module of Node
    • Node core module JS implementation
    • Node core module c++ implementation, js package call c++ module
  • Third-party modules, or user-written modules
    • JavaScript module, we developed to write JavaScript module
    • JSON module, a JSON file
    • C/C++ extension module, written in C/C++, compiled with the suffix.node (interested in the dynamic link library)

2.3.1 Core module of a node

(1) c + + binding loaders:
(introduction of c++ core modules, exposed c++ interfaces)
Process. The binding () :The older C ++ binding loader is accessible from user space because it is an object attached to the global process object. These C ++ bindings are created using NODE_BUILTIN_MODULE_CONTEXT_AWARE () and their NM_FLAGS are set to NM_F_BUILTIN. We can’t guarantee the stability of these bindings, but we still have to deal with the compatibility issues they cause from time to time.
Process. _linkedBinding () :Add additional C ++ bindings to your application. These C ++ bindings can be created using NODE_MODULE_CONTEXT_AWARE_CPP () with the flag NM_F_LINKED.
InternalBinding () :Private internal C ++ binding loader, (cannot be accessed from user area unless ‘require (‘internal/test/binding’)’). These C ++ bindings are created using NODE_MODULE_CONTEXT_AWARE_INTERNAL (), whose NM_FLAGS is set to NM_F_INTERNAL.
2 Internal JavaScript module loader:
This module is the smallest module system for loading JavaScript core modules in lib/**/*.js and deps/**/*.js.
All core modules are compiled into Node binaries using node_javascripts. Cc generated by js2c.py, which makes them quicker to load without I/O costs.

This class makes lib/internal / *, deps/internal / * modules and internalBinding () available by default for core modules, And allow the core module to reference itself by require (‘internal/bootstrap/loaders’), even if the file is not written in CommonJS style.
InternalBinding is an alternative to process.binding.

Process.binding/InternalBinding is actually a C++ function that Bridges the C++ and Javascript sides of the Node library.

2.3.2 Non-core module of a node

  • JavaScript modules, JavaScript modules that we develop and write (or third-party modules)
  • JSON module, a JSON file
  • C/C++ extension module, written in C/C++, compiled with the suffix.node (interested in the dynamic link library)

The general loading process of such modules is as follows:



Path analysis

const filename = Module._resolveFilename(request, parent, isMain);Copy the code

Is there a cache

 const cachedModule = Module._cache[filename];
  if(cachedModule ! = =undefined) {
    updateChildren(parent, cachedModule, true);
    return cachedModule.exports;
  }Copy the code

Create a Module object

  const module = new Module(filename, parent);
// Cache module objects
  Module._cache[filename] = module;Copy the code

File location is executed according to the suffix

// Native extension for .js
Module._extensions['.js'] = function(module, filename) {
  if (experimentalModules && filename.endsWith('.js')) {
    const pkg = readPackageScope(filename);
    if (pkg && pkg.type === 'module') {
      throw newERR_REQUIRE_ESM(filename); }}const content = fs.readFileSync(filename, 'utf8');
  module._compile(stripBOM(content), filename);
};


// Native extension for .json
Module._extensions['.json'] = function(module, filename) {
  const content = fs.readFileSync(filename, 'utf8');

  if (manifest) {
    const moduleURL = pathToFileURL(filename);
    manifest.assertIntegrity(moduleURL, content);
  }

  try {
    module.exports = JSON.parse(stripBOM(content));
  } catch (err) {
    err.message = filename + ':' + err.message;
    throwerr; }};// Native extension for .node
Module._extensions['.node'] = function(module, filename) {
  if (manifest) {
    const content = fs.readFileSync(filename);
    const moduleURL = pathToFileURL(filename);
    manifest.assertIntegrity(moduleURL, content);
  }
  // Be aware this doesn't use `content`
  return process.dlopen(module, path.toNamespacedPath(filename));
};Copy the code

Returns the module.exports object.

3. Summary

The running mechanism of node modules is simple to understand. Involve the general process, skip the underlying system distinction.

The article sorted out the relevant materials, recorded part of the practice and their own understanding, understanding of the inaccurate, but also ask for correct. Welcome to discuss and study together.

References:

Analyze the loading and running principle of Node.js module with source code
The require method of a module instance
C++ and JS interaction
Develop customizable applications using the Google V8 engine
The node source
Simple Node