Home  >  Article  >  Web Front-end  >  A brief analysis of the working principle of require in Node.js_node.js

A brief analysis of the working principle of require in Node.js_node.js

WBOY
WBOYOriginal
2016-05-16 16:43:121361browse

Almost any Node.js developer can tell you what the require() function does, but how many of us actually know how it works? We use it every day to load libraries and modules, but its behavior is a mystery to us.

Out of curiosity, I dug into node's core code to find out what was going on under the hood. But this is not a single function. I found module.js in node's module system. This file contains a surprisingly powerful and relatively unfamiliar core module that controls the loading, compilation and caching of each file. `require()`, its emergence is just the tip of the iceberg.

module.js

Copy code The code is as follows:

function Module(id, parent) {
this.id = id;
this.exports = {};
This.parent = parent;
// ...

Module.js mainly plays two roles within Node.js. First, it provides a foundation for all Node.js modules. Each file is a new instance of a base module that persists even after the file has been run. That's why we can attach attributes to module.exports and return them when needed.

The second major task of this module is to handle the module loading mechanism of node. The independent operation "require" function we use is actually an abstract concept of module.require, which itself is just a simple encapsulation of the Module._load function. This load method handles the actual loading of each file and starts our journey there.

Module._load

Copy code The code is as follows:

Module._load = function(request, parent, isMain) {
// 1. Check Module._cache for the cached module.
// 2. Create a new Module instance if cache is empty.
// 3. Save it to the cache.
// 4. Call module.load() with your the given filename.
// This will call module.compile() after reading the file contents.
// 5. If there was an error loading/parsing the file,
// Delete the bad module from the cache
// 6. return module.exports
};

Module._load is responsible for loading new modules and managing module cache. Caching each module loaded reduces the number of redundant file reads and can significantly speed up your application. Additionally, shared module instances allow singleton features of modules to remain in the project's state.

If a module does not exist in the cache, Module._load will create a new base module of that file. It then tells the module to read the contents of the new files before sending them to module._compile. [1]

If you notice step #6 above, you will see that module.exports has been returned to the user. This is why when you define a public interface to use, you use exports and module.exports, because Module._load will next return the required content. I'm surprised there aren't more features here, but it would be nice if there were.

module._compile

Copy code The code is as follows:

Module.prototype._compile = function(content, filename) {
// 1. Create the standalone require function that calls module.require.
// 2. Attach other helper methods to require.
// 3. Wraps the JS code in a function that provides our require,
// module, etc. variables locally to the module scope.
// 4. Run that function
};

· This is where the real magic happens. First, a special stand-alone require function is created for this module. This is a feature we all need and are familiar with. The function itself is just a package in Module.require, and it also contains some little-known auxiliary methods that are easy for us to use:

· require(): Load an external module
· require.resolve(): Resolve a module name to its absolute path
· require.main:main module
· require.cache: all cached modules
· ·require.extensions: The available compilation methods for each valid file type according to its extension

Once require is ready, the entire loaded source code is encapsulated in a new function that can accept require, module, exports and all other exposed variables as parameters. This is a function created solely to encapsulate the module in order to prevent conflicts with the Node.js environment.

Copy code The code is as follows:

(function (exports, require, module, __filename, __dirname) {
// YOUR CODE INJECTED HERE!
});

The Module._compile method is executed synchronously, so the call to Module._load can only wait until the end of this code and return module.exprts to the user.

Conclusion

So, we have understood the entire code of require and have a preliminary understanding of how it works.

If you've followed all this, then you're ready for the final secret: require('module'). That's right, the module system itself can be loaded through the module system. Inception. This may sound strange, but it allows user space to interact with the module loading system without having to delve into Node.js core. Popular modules are built like this. [2]

If you want to know more, check out the module.js source code yourself. There are enough things to give you a headache for a while. Can the first one tell me what is NODE_MODULE_CONTEXTS" and why it is added and people who add it can get bonus points :)

[1] The module._compile method is only used to run JavaScript files. JSON files need to be parsed through JSON.parse() and returned

[2] However, both modules are built on private module methods such as Module._resolveLookupPaths and Module._findPath. You can think it's not much better...

Statement:
The content of this article is voluntarily contributed by netizens, and the copyright belongs to the original author. This site does not assume corresponding legal responsibility. If you find any content suspected of plagiarism or infringement, please contact admin@php.cn