This is the 10th day of my participation in Gwen Challenge

One of the most frequently used methods in NodeJS is the require method. NodeJs follows the CommonJS specification, the core of which is to load other dependent modules via require.

A few questions

  1. Module. exports or exports is a global variable?
  2. Are modules loaded synchronously or asynchronously?
  3. Will circular references cause performance problems or errors?

What is a CommonJS

Each file is a module, with its own independent scope, variables, methods, etc., invisible to other modules. The CommonJS specification states that within each module, the module variable represents the current module. This variable is an object whose exports property (module.exports) is the interface to the outside world.

Node module classification

  1. Build-in modules — modules provided in C++ in Nodejs.
  2. Constant module — The module in Nodejs that defines constants.
  3. Native Module — modules provided in javascript form in Nodejs.
  4. Third Party Module – modules provided by third parties.

The module object

NodeJs provides a Module builder function inside. All modules are instances of Module.

Inside each module, there is a Module object that represents the current module. It has the following properties.

  • Properties of the Module object

    • module.idThe module’s identifier, usually the module’s file name with an absolute path.
    • module.filenameThe file name of the module, with an absolute path.
    • module.loadedReturns a Boolean value indicating whether the module has finished loading.
    • module.parentReturns an object representing the module that called the module (null for module.parent in the program entry file)
    • module.childrenReturns an array representing other modules used by this module.
    • module.exportsRepresents the output value of the module.
  • The module.exports property refers to the interface that the module exports. Other files that load the module actually read the module.exports variable. The module.exports property represents the interface that the current module exports. Other files that load the module actually read the module.exports variable.

  • Exports variable

We sometimes write things like:

// test.js
function test(){
    console.log(test);
}
exports.test = test;

// result.js
const test = require("./test")
Copy the code

This also gets the correct result because the: exports variable points to module.exports. This equates to a line of command in the header of each module.

var exports = module.exports;
Copy the code

Note: you cannot assign a value to the exports variable directly, which would change the direction of exports from module.exports. Exports cannot be returned if the require method is used on any other module because the require method gets the module.exports value of any other module.

Suggestion: Export results using module.exports whenever possible.

Module flow

  • Create a module
  • Export module
  • Load module
  • Use the module

Method the require

Require is node’s method for loading and executing modules that export other files.

In NodeJs, each Module we introduce corresponds to a Module instance, including the entry file.

Complete steps:

  1. Call the parent module’s require method (the parent module is the current module that calls the module)

    require = function require(path) {
      return mod.require(path);
    };
    Copy the code
  2. Call Module’s _load method

  3. Run the module. _resolveFilename command to obtain the Module path fileName

    const filename = Module._resolveFilename(request, parent, isMain);
    Copy the code
  4. Check whether a cache for the module exists according to fileName

    • Call if a cache existsupdateChildrenMethod updates the cache contents and returns the cache
    • If no cache exists, the execution continues
  5. As a native module, the loadNativeModule method is called to load it

    • If the load succeeds, the native module is returned
    • Otherwise, continue
  6. Generates a Module instance based on the current Module name (path) and the parent Module object:

    const module = cachedModule || new Module(filename, parent);
    Copy the code
  7. Then determine whether the module is an entry file

    if (isMain) {
        process.mainModule = module;
        module.id = '. ';
    }
    Copy the code
  8. Store an instance of the Module in the Module cache

    Module._cache[filename] = module;
    Copy the code

  9. The instance of the module calls its own load method to load the module according to fileName

    module.load(filename);
    Copy the code
  10. Gets the suffix name of the module file

    const extension = findLongestRegisteredExtension(filename);
    Copy the code

    If the suffix name is in ES Module format (.mjs), determine whether the Module supports.mjs file parsing, and throw an exception if it does not.

  11. Resolve module file contents by suffix name

    Module._extensions[extension](this, filename);
    Copy the code
  12. Read the file content according to fileName

    content = fs.readFileSync(filename, 'utf8');
    Copy the code
  13. Compile and execute the read file, calling module’s _complile method:

    module._compile(content, filename);
    Copy the code

    _compile main content steps:

    const compiledWrapper = wrapSafe(filename, content, this);
    const dirname = path.dirname(filename);
    const require = makeRequireFunction(this, redirects);
    let result;
    const exports = this.exports;
    const thisValue = exports;
    const module = this;
    result = compiledWrapper.call(thisValue, exports.require.module, filename, dirname);
    return result;
    Copy the code

    The return value of the wrapSafe method

    The code to obtain the above result is:

    const wrapper = Module.wrap(content);
    return vm.runInThisContext(wrapper, {
        filename,
        lineOffset: 0.displayErrors: true.importModuleDynamically: async (specifier) => {
            const loader = asyncESM.ESMLoader;
            returnloader.import(specifier, normalizeReferrerURL(filename)); }});Copy the code
  14. Change the loading status of this module to true

    this.loaded = true;
    Copy the code
  15. Loading succeeded.

conclusion

Through the above debugging process, the following conclusions can be drawn:

  1. In NodeJs, everything is Module from the entry file.
  2. Modules are loaded synchronously.
  3. Because of the caching mechanism, cyclic references to modules have little impact on performance, and cyclic references to modules can be incomplete and can cause errors
  4. The require module lookup process is as follows:

  1. The flowchart for resolving file paths is as follows:

In this paper, the

Learn interesting knowledge, meet interesting friends, shape interesting soul!

Everybody is good! I am the author of programming Samadhi, yi Wang, my public account is “programming Samadhi”, welcome to pay attention to, I hope you can give me more advice!

Knowledge and skills should be paid equal attention to, internal force and external power should be repaired simultaneously, theory and practice should grasp both hands, both hands should be hard!