preface

Modularity is a magic thing, there are many languages in modularity, what is the principle of modularity, presumably many small partners are very interested in, today we will write node require.js to let you a deeper understanding of modularity

The full code can be found at the Github codebase: github.com/Y-wson/Dail…

Simple example

So let’s create file A

let a = 12;
a = a + 1;
module.exports = a;
Copy the code

Create a file B

const a = require("./v");

console.log(a);
Copy the code

Module. Exports = module. Exports = module. Exports = module. Exports = module

Let’s start with some basic concepts

Module classification

  1. Built-in module: is what Node.js provides natively, for examplefs.httpWait, these are the core modules of Node.js, which are loaded when the Node.js process starts.
  2. File modules: third-party modules and custom modules

Execute the process

1.Path analysis: Determines the module location based on the identifier

  1. The built-in modules are loaded preferentially, even if there is a file with the same name.
  2. Not a built-in module. Go to the cache first.
  3. If the cache does not exist, find the file in the corresponding path.
  4. If no corresponding file exists, load this path as a folder.
  5. If you can’t find any files or folders, gonode_modulesFind below.
  6. I can’t find it yet.

Load folders

If you can’t find a file, find a folder, but it is not possible to load the entire folder. There is also a loading order for the folder:

  1. Let’s see if there’s anything under this folderpackage.jsonIf there are, look insidemainFields,mainIf the field has a value, load the corresponding file. So if you’re looking at some third-party library source code and you can’t find the entry, look it uppackage.jsonThe inside of themainFields, for examplejquerythemainThe field looks like this:"main": "dist/jquery.js".
  2. If there is nopackage.jsonorpackage.jsonThere is nomainJust looking forindexFile.
  3. If you can’t find either of these steps, you’re reporting an error.

2.File location: Determines the specific file and file type in the target module

Supported file types

Require supports three main file types:

  1. .js:.jsFile is the most commonly used file type, loading will first run the entire JS file, and then the above mentionedmodule.exportsAs arequireThe return value of.
  2. .json:.jsonFile is a plain text file, used directlyJSON.parseJust return it as an object.
  3. .node:.nodeFiles are C++ compiled binaries, a type that pure front ends rarely touch.

3.Compile implementation: Completes file compilation and execution in the corresponding mode

We will use vm to execute files

Handwritten source

All the functionality a Module loads is in the Module class. To distinguish our Module from the native Module class, we call our Module MyModule

First create a myModule.js file and then initialize our file

function MyModule(id = "") {
    this.id = id;
    this.exports = {};
}

MyModule._extension = {
    ".js": () = > {},
    ".json": () = >{}}; MyModule.require =(request) = > {
    console.log(request);
};
Copy the code

Next, we will do the first step of path analysis. Here we are just a small white level tutorial, so we will only cover loading custom modules

MyModule.resolveFileName = (request) = > {
    let filename = path.resolve(request);
    const extname = path.extname(filename);
    if (extname) {
        return filename;
    } else {
        const suffixArr = Object.keys(MyModule._extension);
        for (let suffix of suffixArr) {
            filename = `${filename}${suffix}`;
            if (fs.existsSync(filename)) {
                returnfilename; }}}};Copy the code

Then we check to see if the module is in the cache. If so, we load it directly. If not, we load the module

MyModule._cache = [];

MyModule.require = (request) = > {
    const filename = MyModule.resolveFileName(request);
    // Check whether there are modules in the cache
    const cacheModule = MyModule._cache[filename];
    if (cacheModule) return cacheModule.exports;
    // Initialize a new module
    const module = new MyModule(filename);
    // Load caches the module before, so that if there are any circular references it will be returned to the cache, but the exports in the cache may not be present or complete
    MyModule._cache[filename] = module;
    module.load(module);
    // returns module exports
    return module.exports;
};
Copy the code

The load function is actually our second step, determining the file type and executing the corresponding method to load the file

MyModule.load = (module) = > {
    const extname = path.extname(module.id);
    // Execute file contents according to the suffix
    MyModule._extension[extname](module);
};
Copy the code

Next comes the execution file. First we define how to execute the js file

__filename, __dirname,require, etc. These are passed by functions and are not true global variables

// Define an anonymous function

MyModule._wrapper = [
`(function(exports,require,module,__filename,__dirname){`.` `}),]; MyModule._extension = {".js": (module) = > {
        // Get the file contents
        const content = fs.readFileSync(module.id, "utf-8");
        // Prepare global variables to pass inside the module
        const filename = module.id;
        const dirname = path.dirname(filename);
        // VM is a library for executing strings
        const fun = vm.runInThisContext(
        MyModule._wrapper[0] + content + MyModule._wrapper[1]); fun.call(module.exports,
        module.exports,
        MyModule.require,
        module,
        filename,
        dirname
    );
},
".json": (module) = >{}};Copy the code

Json files are much easier to execute than JS files

".json": (module) = > {
    const content = fs.readFileSync(module.id, "utf-8");
    module.exports = JSON.parse(content);
},
Copy the code

Well, we’re done with our own MyModule

You can test it by referencing it yourself

conclusion

  • 1. Handwritten require source code of three steps 1. Path analysis 2. Compile implementation
  • 2. Compilation and execution actually use the vm library to execute strings, which is better than eval, function, etc
  • 3. In each moduleexports, require, module, __filename, __dirnameNone of the five parameters are global variables, but are injected when the module is loaded.

reference

Drill into node.js’s module loading mechanism by writing the require function