There was a time when Javascript wasn’t so awesome that almost everyone thought it was just ascript for making special effects on the web. There was a lot of teasing about the shortcomings of javascript, which was hastily created at the time. With the development of the Web, Javascript is now more and more widely used.
Despite Javascript’s efforts, one important feature is still missing: modules. After all, Python has require, and PHP has include and require. The way js is introduced via <script> tags is fine, but it lacks organization and constraints, and is hard to secure and easy to use. So the CommonJS specification is nothing short of revolutionary.
Now let’s talk about the CommonJS module specification and implementation in Node.
CommonJS module specification
The CommonJS module definition is divided into three parts: module definition, module reference and module identity.
- Module definition:
First create an A.js file and write:
module.exports = 'hello world';
Copy the code
In Node, a file is a module, and the module.exports object exports methods or variables of the current module. This code simply exports the string Hello world, and the context provides the require() method to import the external module.
- Module reference:
Create a b.js file and write:
let b = require('./b.js'); //.js can be omitted, which we'll talk about later on console.log(b); // hello worldCopy the code
3) Module identifier: The module identifier is a parameter in the require() method, which can be used as. And.. A relative path at the beginning, or an absolute path. You can do it without a.js file name suffix, and we’ll talk about why.
Module implementation of Node
The code above is the simplest use of modules. So what is the process behind these simple lines of code? Let’s break it down a little bit.
Node introduces modules through three steps: path analysis, file location, and compilation execution
There are two types of modules in Node: core modules provided by Node and user-written file modules.
Some of the core modules are directly loaded in memory, so when introducing this part of the module, the file definition and compilation execution can be omitted, priority judgment in path analysis, and the loading speed is also the fastest.
It is also important to know that Node introduces modules that are cached to reduce the overhead of secondary references. It caches compiled and executed objects, rather than files, as browsers do.
-
Path analysis as stated above, the parameter in the require() method is called the module identifier, and path analysis is based on the identifier. Module identifiers in Node fall into the following categories:
- Core modules, such as HTTP, FS, PATH, etc.
- . Or.. Start the relative path file module.
- An absolute path file module starting with /.
- Non-path file modules, such as the custom CONNECT module.
-
Core modules are second in priority to cache loading, which is the fastest since it has been compiled into binary code in Node source compilation.
-
When the relative path file module analyzes the path, the require() method will convert the path to the real path and index the absolute path. The result of the compiled execution will be cached to make the secondary reference load faster.
-
Custom modules are special file modules, which can be in the form of files or packages, and are the slowest to find.
Node’s search strategy for locating specific files in a file module can be represented as an array of paths.
Console. log(module.paths) in the js file; Execute in any directory; You get an array like this:
[ '/Users/lq/Desktop/node_modules', // node_modules in the current file directory'/Users/lq/node_modules'// Node_modules directory in the parent directory'/Users/node_modules'// Node_modules in the parent directory'/node_modules']// Recurse up the path until you find node_modules in the root directoryCopy the code
The deeper the path of the current file, the more time it takes to find a module, which is why custom modules are slow to load.
- File location
Require () may not pass file extensions when parsing identifiers. The CommonJS module specification allows this, but Node supplements the extension name in the order of.js,.json, and.node. The fs module is called in turn to block synchronously to determine whether the file exists.
Along the way, Node provides some support for the CommonJS module specification. First, Node looks for the package.json file in the current directory, uses json.parse () to parse out the package.json object and locate the package by retrieving the file name specified by the main attribute. If the main property specifies the wrong file name or there is no package.json file, Node will use index as the default file name and look for index.js, index.json, and index.node in sequence. If the array of paths is traversed and still not found, an error is thrown.
- Module compilation After locating a specific file, Node creates a new module object and compiles it according to the path. Different methods are used depending on the extension name.
- If it is a. Js file, the fs module synchronously reads the file and compiles it
- If it is a. Json file, the fs module synchronously reads the file and uses json.parse () to parse the returned result
- If it is a.node file, use the dlopen() method to load the file generated by the last compilation. This is C/C++ prepared to expand the file, I will be ignored in the process of the realization of the principle.
With that said, let’s jump right into the implementation
Create an A.js file and write:
module.exports = 'hello world';
Copy the code
Create a new b.js and write:
let b = require('./a.js');
console.log(b); //hello world
Copy the code
The printed result is Hello word. This is Node’s built-in require method. Now let’s implement our own require method.
Let’s modify it directly in B. Js:
// Import Node core moduleslet fs = require('fs');
let path = require('path');
let vm = require('vm');
functionModule(p) { this.id = p; // The identity of the current module, which is the absolute path this.exports = {}; // Each module has an exports property, add a this.loaded =false; // Wrap the contents of the file module.wrapper = ['(function(exports,require,module){'.'}) '] // All load policies module._extensions = {'.js': function(module) {// Read the js file and add a closurelet script = fs.readFileSync(module.id, 'utf8');
letfn = Module.wrapper[0] + script + Module.wrapper[1]; Module. exports, module.exports, myRequire, module); // Run this context (fn). Call (module.exports, module. // Do not pollute globally with the runInThisContext() methodreturn module.exports;
},
'.json': function (module) {
return JSON.parse(fs.readFileSync(module.id, 'utf8')); Module._cacheModule = {} // Store cache module. _resolveFileName =function(moduleId) {// Return an absolute path method based on the path argument passed inlet p = path.resolve(moduleId);
if(! Path.extname (moduleId)) {// If no file suffix is passedletarr = Object.keys(Module._extensions); // Convert the key of an object to an arrayfor (leti = 0; i < arr.length; I++) {// suffixes the arrayletfile = p + arr[i]; try { fs.accessSync(file); // Check if the file exists and return if it doesreturnfile; } catch (e) { console.log(e); // There is no error}}else {
returnp; // If the file suffix has already been passed, return the absolute path}} module.prototype.load =function(filepath) {// Module loading methodlet ext = path.extname(filepath);
let content = Module._extensions[ext](this);
return content;
}
functionMyRequire (moduleId) {// A custom myRequire methodletp = Module._resolveFileName(moduleId); // Convert the module identifier passed in to an absolute pathif(module._cachemodule [p]) {// If the Module already existsreturnModule._cacheModule[p].exports; // Return the compiled and executed object directly}letmodule = new Module(p); // If the module does not exist, create a new module objectletcontent = module.load(p); Module._cacheModule[p] = Module; module.exports = content;return module.exports;
}
let b = myRequire('./a.js');
console.log(b);
Copy the code
MyRequire () = hello world = hello World = hello World = hello world = hello world = hello world = hello world Of course, module source more than so much, interested in their own view. This article only explains the module loading principle. There is not enough rigorous writing place, hope understanding. If there are mistakes, can be pointed out, fixed timely modification.
reference
Part of the content is compiled according to the book “Simple Node.js”