Go on about CommonJS for a minute

Common knowledge is Common knowledge, Common knowledge is Common knowledge, Common knowledge is Common knowledge, Common knowledge is Common knowledge, Common knowledge is Common knowledge. Obviously not. This is not common sense. I originally thought that commonJS was an open source JS library that was easy to use and filled with common front-end methods. However, I was wrong. Not only is commonJS not a library, but it is an invisible thing. Like school discipline, used to standardize JS programming, shackle the front end. Like Promise, it is a specification, and while there are many open source libraries that implement these specifications, this specification can also be implemented with our JS capabilities.

CommonJs specification

So what does CommonJS regulate? To explain this specification, start with the nature of JS. JS is a literal scripting language that is compiled and run at the same time, so there is no concept of modules. So CommonJS is a specification that exists in order to perfect the lack of JS in this respect.

CommonJS defines two main concepts:

  • requireFunction to import the module
  • module.exportsVariable used to export the module

However, browsers don’t support either of these keywords, so I think that’s why browsers don’t support CommonJS. If you want to use CommonJs in a browser, you need a compiler library like Browserify to help you build CommonJs into a browser-supported syntax that implements require and exports.

So what can CommonJS be used for? Although CommonJS cannot be used directly in browsers, NodeJS can be implemented based on the CommonJS specification. In NodeJS, we can import and export modules directly using the two keywords require and exports.

Implementation of CommomJS module in Nodejs

require

Let {count,addCount}=require(“./utils”) So what happens when you import? First of all, we must parse the path. The system gives us an absolute path. The relative path we write is for us to see, and the absolute path is for the system to see. So the first thing require does is parse the path. We can write very succinctly, only need to write the relative path and file name, even the suffix can be omitted, ask require to help us match to find. The first step in require is to parse the path to get the contents of the module:

  • If it’s a core module, for examplefs, directly back to the module
  • If it is with a path such as/.. /And so on, concatenate an absolute path and read the cache firstrequire.cacheThen read the file. If no suffix is added, it is automatically suffixed and identified one by one.
    • .jsParse to a JavaScript text file
    • .jsonParsing JSON objects
    • .nodeParse to a binary plug-in module
  • Modules are cached after first loadingrequire.cacheIn, so multiple loadsrequire, the resulting object is the same.
  • When executing module code, the module is wrapped in the following mode so that the scope is within the module.
(function(exports, require, module, __filename, __dirname) {// module code is actually here});Copy the code

Nodejs official explanation, you can refer to

module

Having said what require does, what does the module that require triggers do? We look at usage, first to write a simple export module, write the module, only need to export parameters, join the module. Exports.

let count=0
function addCount(){
    count++
}
module.exports={count,addCount}
Copy the code

And then add according to require when executing code, so our code actually looks like this:

(function(exports, require, module, __filename, __dirname) {
    let count=0
    function addCount(){
        count++
    }
    module.exports={count,addCount}
});
Copy the code

What is happening to the module when require? We can interrupt it in vscode:

Based on this breakpoint, we can sort out:

In yellow, require, which is the method we’re calling

The work of the Module circled in red

Module._compile Module.extesions.. js Module.load tryMouduleLoad Module._load Module.runMainCopy the code

Circled in blue is what NodeJS does, which is the NativeModule that executes module objects.

We all know that in JS, the function is called in the stack mode, i.e., near before out, i.e., require this function is triggered, the runtime in the figure is run from bottom to top. So the blue box runs first. I’ll dig up some of his code, study it.

NativeModule native code key code, which is used to encapsulate modules.

NativeModule.wrap = function(script) {
    return NativeModule.wrapper[0] + script + NativeModule.wrapper[1];
};

NativeModule.wrapper = [
    '(function (exports, require, module, __filename, __dirname) { '.'\n}); '
];
Copy the code

Once the NativeModule triggers module. runMain, our modules will start loading. Let’s read from bottom to top.

  • Module._load, is to create a new onemoduleObject, and then put the new object inModuleIn cache.
    var module = new Module(filename, parent);
    Module._cache[filename] = module;
    Copy the code
  • tryMouduleLoadAnd then the new onemoduleObject starts parsing the imported module content
        module.load(filename);
    Copy the code
  • The newmoduleThe module. load object inherits from the module. load method, which parses the type of the file and executes it separately
  • Module.extesions.. jsThis does two things: it reads the file, and it’s ready to compile
  • Module._compileSo how does JS run text? There are three ways for javascript to turn text into an executable:
    • The eval method eval (” the console. The log (” aaa “) “)

    • New Function() template engine

      let str="console.log(a)"
      new Function("aaa",str)
      Copy the code
    • Node executes strings. We use advanced VMS

      let vm=require("vm")
      let a='console.log("a")'
      vm.runInThisContext(a)
      Copy the code

      Here the Module is compiled as a VM, first wrapped, then executed, and finally returned to require, so we can get the result of execution.

      var wrapper = Module.wrap(content);
      var compiledWrapper = vm.runInThisContext(wrapper, {
          filename: filename,
          lineOffset: 0,
          displayErrors: true
      });
      Copy the code

Because all modules are executed after encapsulation, the imported module can only be accessed through the module.exports interface.

To summarize

The _load method for Module is to create a cache of new modules in the current Module, so that the next time you require it, you can return to it without executing it again. Then the new Module’s load method loads and executes code via the VM to return the object to require.

Because of this, if the value of export is a parameter rather than a function, the change of the value of the current parameter will not cause the change of Export, because the parameter assigned to export is static and will not cause a second run.

CommonJs module and ES6 module differences

Usage scenarios

CommonJS is mostly used on the server side because of the limitations of the keyword. For ES6 module loading, there are already browsers that support this feature, so ES6 can be used in browsers. If you encounter a browser that does not support ES6 syntax, you can choose to translate it into ES5.

Syntax differences

ES6 is also a JavaScript specification. The difference between ES6 and CommonJs module is obvious. Firstly, the code is not the same.

commonJS ES6
Supported keywords arguments,require,module,exports,__filename,__dirname import,export
The import const path=require("path") import path from "path"
export module.exports = APP; export default APP
Imported objects modify Can’t be modified at will
The import number Feel free torequire, but except for the first time, everything else is fetched from the module cache Import in the header

** Attention everyone! To highlight! Nodejs is the CommonJS parent, so some ES6 features are not supported, such as the module import and export keywords. If you run nodeJS, expect a red error

Load difference

In addition to syntax differences, the nature of the modules they reference is different. Although both are modules, the structure of the modules varies greatly.

In ES6, if you want to test in a browser, you can use the following code:

//utils.js
const x = 1;
export default x
Copy the code
<script type="module">
    import x from './utils.js';
    console.log(x);
    export default x
</script>
Copy the code

The first thing to do is to give the script type=”module” to indicate that it is an ES6 module, and this tag is loaded asynchronously by default, meaning that the page is fully loaded before being executed. The code will not run without this tag. Then you can write import and export directly.

Several problems with ES6 module import:

  • The same module can only be introduced once, for examplexIf it is already imported, it cannot be imported from utilsx
  • Different modules introduce the same module, this module will only be the first timeimportIn the execution.
  • The introduced module is a reference to a value and is dynamic, changing other related values as well
  • Do not break the links of imported objects, such as minecountI can’t change it, because it’s imported, and I can only change it incountModify the module where it resides. But if thecountIs an object, so you can change the properties of the object, for examplecount.one=1But don’tcount={one:1}.

If you look at this example, I wrote a little test to change the value of object, and you can see that the initial value of count in utils.js should have been 0, but I ran addCount so the value of count changed dynamically, so the value of count became 2.

let count=0
function addCount(){
    count=count+2
}
export {count,addCount}
Copy the code
<script type="module">
    import {count,addCount} from './utils.js'; AddCount () console.log(count); </script>Copy the code

Contrast this with the commonJS module reference, which has the following features:

  • As explained in the previous section, a fixed value exported by a module is a fixed value and will not change due to subsequent modifications, unless instead of exporting a static value, a function is called dynamically every time it is called, and the value is updated every time.
  • The imported object can be modified at will and is just a copy of the imported module.

If you want to further study, you can refer to Ruan’s ES6 introduction – Module load implementation.

CommonJS module summary

CommonJS modules can only be run in environments that support the CommonJS specification. Nodejs is developed based on the CommonJS specification, so it can run perfectly. Nodejs does not support ES6 module specifications. So nodeJS server development is usually written using the CommonJS specification.

CommonJS modules import require and export module.exports. Note that if the exported object is a static value and the value is very large, it may be changed later. Use functions to dynamically obtain the exported object; otherwise, the modified value cannot be obtained. The imported parameters can be changed at will, so be careful when using them.