preface

Modules are an integral part of Node and the basis of server-side programming. While organizing the module, the first part of the Node module encapsulation and so on to do a summary. I hope I can actually help you. This article will do a comprehensive collation of CommenJS specification, Node file module and core module. If you like my article, welcome to comment, welcome to Star~. Welcome to my Github blog

The body of the

I think the emergence of modules is the biggest improvement in JS. Because of modules, a lot of great things can be shared without worrying about variable contamination and namespaces.

Node as a server-side javascript, it borrows from CommonJS specifications to form a set of easy-to-use module specifications.

First, take a look at this most common example:

//circle.js

const { PI } = Math;

exports.area = x= > PI*x**2;
exports.circle = x= > 2*PI*x;Copy the code
//main.js

const circle = require('./circle');

console.log(circle.area(4));   / / 50.26548245743669Copy the code

In fact, we can clearly see that the module specification part of the two files can be divided into three parts:

  1. Require (module reference) => This is the most core part of the entire module system, can introduce other modules, full use.
  2. Exports => Another cool place to export the contents of your module for use by other modules.
  3. Identification (module identification) => Something for others to identify the module.

These three pieces can be summarized in a picture:

As shown in figure:

module

From this diagram, we can see that modules can export interfaces to each other through exports and then import content from another module through require.

This gives us a rough idea of what a module is. It is mainly divided into three parts: module reference, module definition and module identification.

However, what we need to know most about the entire module section is the require mechanism. Node has a lot to appreciate about the require implementation.

First, you need to understand the steps introduced throughout the module. From the above example, you can see the three parts:

  1. Path analysis => For example analysis, ‘./circle’ is the path. (./ Such are relative paths and, of course, absolute paths)
  2. Locate the file => Obtain the file based on the analyzed location.
  3. Compile execution => Only files that have been compiled can be used in other modules (how to compile will also be analyzed later).

Sometimes, circumstances are unique. Modules themselves are divided into core modules and file modules. The core modules are compiled in the Node source code. Is compiled into a binary file. And some core modules are directly loaded into memory when the Node process starts, so the introduction of this part of the core module does not need to go through the steps of file location and compilation execution.

And the special part is in the cache. After each module is loaded for the first time, Node caches its compiled objects for secondary loading. As a result, the secondary load takes precedence over the cache, and modules loaded from the cache do not require file location and compilation.

Path analysis alone can be divided into three different ways:

  1. Core modules, such as HTTP, FS, path, etc., have a second priority after cache loading and are compiled directly into binary files
  2. Path modules, such as’./circle ‘in the example above. The path is clear, the search speed is relatively fast, and the loading speed is slower than the core module
  3. Custom modules, mostly files in the form of NPM packages, are stored in node_modules and have no corresponding path. This kind of search is tedious.

    The search method is as follows: 1. Check whether the corresponding module exists in node_modules in the current directory. 2. Otherwise, node_modules in the parent directory is searched until node_modules in the root directory is found. This way is the slowest.

Analyze file location again:

The identifier in the first example is ‘./circle’. As you can see, the file identifier does not have a suffix. So how does node position itself? In fact, Node has a default location order: JS, Node, JSON. Js will be identified first, followed by JSON and Node files. So, here’s a tip: it’s faster to add file suffixes when identifying.node and.json files. Why? Node uses fs synchronous blocking to try and load the file one by one. If not, try the next suffix.

And for those custom modules, such as the NPM package. Nodes are also positioned differently. NPM packages typically have package.json files that have a main property that points to the entry file for the entire package. Without these conditions, Node loads index.js, index.json, and index.node by default

Finally, the analysis of module compilation is as follows:

First, compile execution can also be analyzed by the three suffix file names above:

  1. Js file => after synchronous reading through fs module, compile and execute
  2. Node file =>. Node file is a compilation file of C/C ++ file modules, using dlopen method to load the file introduction.
  3. Json file => Json file is first read by FS, and then compiled and executed by json. parse. Files with other suffixes are treated as JS files. At the same time, we need to take a closer look at the specific process of compiling javascript files.

Javascript file compilation

After fs reads the file, what does Node do with the read content? Does it cause variable pollution? Obviously not. Function (exports, require, module, dirname,filename){} function(exports, require, module, dirname,filename){} function(exports, require, module, filename){} Read the contents}. In this way, it serves as a scope-isolation function and does not pollute the contents of existing modules. Dirname and filename exist in node.

This function code is then executed using the VM native module runInThisContext() method (similar to the eval => code that converts a string into an executable JS). It then returns a concrete object that can be used by the content in the existing module.

C/C++ module compilation

This compilation relies on Node’s process.dlopen() method, and Node uses Libuv for Compatibility with Windows and * Nix platforms. The performance of this module is higher than that of a normal file module, but the writing cost is correspondingly higher.

Json file compilation

Json file compilation is relatively simple: read the file through FS, compile it through the json.parse method, and finally give the content to the variable named in the existing module.

conclusion

So far we have roughly sorted out the overall mechanism of node module, from module export, to import, and identifier analysis. Both can be found in CommonJS, but Node processing is relatively perfect.

If you have any questions about what I wrote, you can comment on it. If I wrote something wrong, you are welcome to correct it. You like my blog, please follow me Star~ yo. We summarize and make progress together. Welcome to my Github blog