Note: 1. The nodeJS source code mentioned in this article is based on V10.14.1 unless otherwise specified

If you are interested in NodeJs, please visit S.E.L.D. or my wechat (w979436427) to discuss your node learning experience

Nodejs implementation of modules

This section is mainly based on NodeJs source code, the implementation of its module to do a brief overview, if there are mistakes, I hope you are not afraid to correct.

When we introduce a module using require, we generally go through two steps: path analysis and module loading

Path analysis

Path analysis is a module lookup process, implemented by the _resolveFilename function.

Let’s expand it through an example:

const http = require('http');
const moduleA = requie('./parent/moduleA');
Copy the code

In this example, we introduce two different types of modules: the core module – HTTP and the custom module moduleA

For the core module, _resolveFilename skips the lookup step and returns directly to the next step

if (NativeModule.nonInternalExists(request)) {
    // Request is the module name 'HTTP'
    return request;
}
Copy the code

For custom modules, there are several cases (_findPath)

  1. File module
  2. The directory module
  3. Load from the node_modules directory
  4. Global directory loading

These are clearly stated in the official documents and will not be repeated here.

If module, then _resolveFilename returns absolute path of the module, such as/Users/XXX/Desktop/practice/node/module/parent/moduleA js.

Load the module

After obtaining the module address, Node starts loading the module.

First, Node checks to see if the module is in the cache:

// filename indicates the absolute path of the module
var cachedModule = Module._cache[filename];
if (cachedModule) {
    updateChildren(parent, cachedModule, true);
    return cachedModule.exports;
}
Copy the code

If the module exists, the corresponding cache content is returned. If the module does not exist, whether it is a core module is further determined:

if (NativeModule.nonInternalExists(filename)) {
    return NativeModule.require(filename);
}
Copy the code

If the module neither exists in the cache nor is a core module, Node instantiates a completely new module object


function Module(id, parent){
  // Usually module absolute path
  this.id = id;
  // What to export
  this.exports = {};
  // Parent module
  this.parent = parent;
  this.filename = null;
  // Check whether the file is successfully loaded
  this.loaded = false;
  / / modules
  this.children = [];
}

var module = new Module(filename, parent);
Copy the code

Node then attempts to load based on the path.

function tryModuleLoad(module, filename) {
  var threw = true;
  try {
    module.load(filename);
    threw = false;
  } finally {
    if(threw) { delete Module._cache[filename]; }}}Copy the code

The loading method is also different for different file extensions.

  • .js files (_compile)

After reading the contents of the file synchronously via FS, wrap it in the specified function:

Module.wrapper = [
  '(function (exports, require, module, __filename, __dirname) { '.'\n}); '
];
Copy the code

Call to execute this function:

compiledWrapper.call(this.exports, this.exports, require.this,
                                  filename, dirname);
Copy the code
  • The json file

After reading the contents of the file synchronously through FS, parse with json.parse and return the contents

var content = fs.readFileSync(filename, 'utf8');
try {
    module.exports = JSON.parse(stripBOM(content));
} catch (err) {
    err.message = filename + ':' + err.message;
    throw err;
}
Copy the code
  • .node

This is an extension file written in C/C++ that loads the file generated by the final compilation through the dlopen() method.

return process.dlopen(module, path.toNamespacedPath(filename));
Copy the code
  • .mjs

This is an extension file for handling ES6 modules and is a new NodeJs feature since V8.5.0. For files with such extensions, you can only import them using the ES6 module syntax import, otherwise an error will be reported (with –experimental-modules enabled)

throw new ERR_REQUIRE_ESM(filename);
Copy the code

If all goes well, the content attached to the Exports object is returned

return module.exports;
Copy the code

Module cycle dependency

Let’s explore the problem of module cyclic dependencies: what happens when module 1 depends on module 2 and module 2 depends on module 1?

We’ll only explore commonJS here

To do this, we create two files, module-a.js and module-b.js, and refer them to each other:

module-a.js

console.log('Start loading module A');
exports.a = 2;
require('./module-b.js');
exports.b = 3;
console.log('Module A is loaded.');
Copy the code

module-b.js

console.log('Start loading module B');
let moduleA = require('./module-a.js');
console.log(moduleA.a,moduleA.b)
console.log('MODULE B is loaded.');
Copy the code

Run module-a.js and you’ll see console output:

Module A Is loaded. Module B is loaded. 2 Undefined Module B is loadedCopy the code

/module-b = /module-b = /module-b = /module-b = /module-b = /module-b = /module-b = /module-b = /module-b = /module-b = /module-b

QA

  1. How do I remove the module cache?

Delete require.cache(moduleId) delete require.cache(moduleId) delete require.cache(moduleId) delete require.cache(moduleId), where moduleId represents the absolute path to the module.

// hot-reload.js
console.log('this is hot reload module');

// index.js
const path = require('path');
const fs = require('fs');
const hotReloadId = path.join(__dirname,'./hot-reload.js');
const watcher = fs.watch(hotReloadId);
watcher.on('change',(eventType,filename)=>{
    if(eventType === 'change') {delete require.cache[hotReloadId];
        require(hotReloadId); }});Copy the code
  1. Can ES6 modules be used in Node?

Starting with version 8.5.0, NodeJs supports native ES6 modules. Two conditions are required to enable this feature:

  1. All file extensions that use ES6 modules must be.mjs
  2. The command line option –experimental-modules node –experimental-modules index.mjs
node --experimental-modules index.mjs
Copy the code

However, as of NodeJs V10.15.0, ES6 module support is still experimental and I do not recommend its use in corporate projects

reference

  1. nodejs-loader.js
  2. Pu Ling. Simple node.js