Modularity in Node is based on commonJS, and the commonJS specification lays out a vision for javaScript to be able to run anywhere. It is the best practitioner of commonJs

Module usage specification

CommonJs module is divided into three parts: module reference, module definition and module identification.

1) The module definition reference is introduced via the require keyword (require is a method defined on the module prototype).

For example, we define a constructor to expose it

function Person (name, age) {

this.name = name

this.age = age

this.sayName = function () {  console.log(this.name)  }

}

module.exports = Person

Then load it in another file

const Person = require('./test')

console.log(Person)

2) Module definition

Each file acts as a separate module, providing require to import external modules. Corresponding to imports, exports provide a unique export for exports, which is a property on Modules. We can mount the export content on exports.

exports.Person = function Person (name, age) {

this.name = name

this.age = age

this.sayName = function () { console.log(this.name) }

}

3) Module identification

This is the file path passed in after require

const Person = require('./test')
Copy the code

Modular implementation of Node

We pass the path of the module when loading, this time will first parse the path, get the absolute path and determine whether this path exists, if not, will prompt an error

Module._resolveFilename = function(id) { let filePath = path.resolve(__dirname, id); If not, try adding the suffix let isexssync = fs.existssync (filePath); if (isExsits) return filePath; Let keys = object.keys (module._extensions); // [.js,.json] for (let i = 0; i < keys.length; i++) { let newFilePath = filePath + keys[i]; If (fs.existssync (newFilePath)) return newFilePath} throw new Error(' module file does not exist ')}Copy the code

When we get the path, we first check whether it has been loaded before. If it has been loaded, we fetch it from the cache. If it is a newly loaded module, we create a new module and store it in the cache.

if(Module._cache[filename]){ return Module._cache[filename].exports; } let module = new module (filename); Module._cache[filename] = module;Copy the code

We then get the file name suffix, perform different load logic, and internally use the FS module’s readFileSync to read the contents of the module

let extname = path.extname(this.id); Module.analysis = {'.js'(Module) {let script = fs.readfilesync (module.id, 'utf8'); Let code = '(function (exports, require, module, __filename, __dirname) {${script}})'; let func = vm.runInThisContext(code); let exports = module.exports; let thisValue = exports let dirname = path.dirname(module.id); func.call(thisValue,exports,req,module,module.id,dirname); }, '.json'(module) { let script = fs.readFileSync(module.id, 'utf8'); Module.exports = json.parse (script)}Copy the code

In general, when we use require to load a module, we will first parse out the path, and then determine whether it has been cached. If it has been cached, we will directly give the last value, and then go to New Module for the first load, and perform different loading logic according to different file name suffixes. The require method gets the module.exports property