Modularization – commonjs, AMD, CMD, ES6

Time:2021-4-14

1、 Modular understanding

1. What is module

  • The complex program is divided into several modules (files) according to certain rules (specifications)
  • The internal data and implementation of the module are private, but some interfaces (Methods) are exposed to communicate with other external modules

2. The evolution of modularity

Global function: encapsulate different functions into different global functions

`Disadvantages: although it can achieve a certain encapsulation effect, a large number of global functions pollute the global namespace and easily cause naming conflicts
function module1 () {
  //...
}
function module2 () {
  //...
}

Namespace: simple object encapsulation

`Disadvantages: reduce the global variables, solve the naming conflict, but the external module can directly modify the internal data
let module = {
  data: 'aaa',
  func () {
    console.log(`${this.data}`)
  }
}
module.data  ='BBB' // modify the internal data of the module directly
module.fn() // bbb

Iife: (self executing function)

`Disadvantages: the data is private, and the external can only be operated by exposed methods. What if the current module depends on another module?
//  module.js File ()
(function (window) {
  let data = 'aaa'
  function func () {
    console.log(`${this.data}`)
  }
  //Exposed interface
  window.module = { func }
})(window)
//  index.html file
<script type="text/javascript"></script>
<script type="text/javascript">
  module.func() // aaa
  console.log ( module.data )// undefined cannot access the internal data of the module
  module.data  ='BBB' // module internal data that cannot be modified
  module.func() // aaa
</script>

Iife enhancement: introducing dependencies

//  module.js file
(function (window, $) {
  let data = 'aaa'
  function func () {
    console.log(`${this.data}`)
  }
  function func2 () {
    $('body').css('background', 'red')
  }
  //Exposed interface
  window.module = { func, func2 }
})(window, jQuery)
//  index.html file
  <! -- JS must be introduced in a certain order -- >
  <script type="text/javascript"></script>
  <script type="text/javascript"></script>
  <script type="text/javascript">
    module.func2()
  </script>

The jQuery library is introduced as a parameter to ensure the independence of modules and make the dependency between modules obvious.

3. The role of modularity

Through the above module splitting, we find that:

  • It reduces global variables and effectively avoids naming pollution
  • Better separation and on-demand loading
  • Improve the reusability and maintainability

But more complex applications, more modules, it is inevitable to introduce more than one<script>In this way, there will be other problems:

  • Too many requests
  • Fuzzy dependence

Modularization certainly has many advantages, but a page needs to introduce multiple JS files, and they have to be introduced in a certain order, which may lead to serious problems in the whole project due to the wrong order of introduction. These problems can be solved by modular specification.

Modular specification

CommonJs

Commonjs node.js According to the common JS specification, each module is a separate scope. In other words, the variables defined in this module cannot be read by other modules.On the server side, modules are loaded synchronously at runtime; on the browser side, modules need to be compiled and packaged in advance.

The core idea is that a single file is a modulerequireMethod to load the dependent modules synchronously, and thenextportsormodule.exportsTo export the interface that needs to be exposed.

// module1.js
var data = 5;
var doSomething = function (value) {
  return value + data;
};
//Exposed interface
module.exports.data = data;
module.exports.doSomething = doSomething;

The above code passedmodule.exportsOutput variablesdataSum functiondoSomething

var example = require('./module1.js');
console.log(example.data); // 5
console.log(example.doSomething(1)); // 6

requireCommand is used to load the module file.requireThe basic function of the command is to read in and execute a JavaScript file, and then return the exports object of the module.

advantage: the reusability of server module. There are nearly 200000 module packages in NPM.

shortcoming: the loading module is synchronous. Only after the loading is completed can the following operations be performed. That is to say, the loading is now in use, which not only causes slow loading speed, but also leads to performance, availability, debugging and cross domain access problems. because Node.js It is mainly used for server programming. Generally, the loaded module files are stored in the local hard disk, which can be loaded quickly without considering the asynchronous loading mode. Therefore, commonjs specification is more applicable. However, it is not suitable for browser environment. Synchronization means blocking loading, and browser resources are loaded asynchronously. In view of the situation of browser, in order to solve the above synchronous loading problem and realize asynchronous loading dependent module, AMD and CMD solutions are available.

AMD (Asynchronous Module Definition

AMD isRequireJSIn the process of promotion, the standardized output of module definition. Amd specification isAsynchronous loading module, which allows you to specify callback functions. Amd highly praises the dependent modulesExecute ahead of timeHowever, since 2.0, requirejs can also be delayed (depending on the writing method, the processing method is different).

Its core interface is as follows:define(id?, dependencies?, factory), which specifies all dependencies when declaring a moduledependenciesAnd pass it as a formal parameterfactoryIn, the dependent modules are executed ahead of time, while the dependent modules are executed ahead of time.

//A.js (define modules without dependencies)
define(function () {
    let data = 'aaa'
    function doSomething () {
        console.log(data)
    }
    Return {dosomething} // exposes the interface
})
//B.js (define dependent modules)
define(['c'], function (c) {
    let data = 'bbb'
    function doSomething () {
        console.log(data + c.getData())
    }
    Return {dosomething} // exposes the interface
})
//C.js (this module is B.js dependent)
define(function () {
    let data = 'ccc'
    function getData () {
        return data
    }
    Return {GetData} // exposes the interface
})
//Introduce dependent modules
Require (['. / a', '. / B'], function (a, b) {// dependency must be written at the beginning
  a.doSomething()
  // ...
  b.doSomething()
  // ...
})
<body>
    <! -- Introduction require.js And specify the entry of JS main file -- >
    <script data-main="./index"></script>
    <script>
        setTimeout(() => {
            console.log('setTimeout')
        }, 0)
    </script>
</body>

Modularization - commonjs, AMD, CMD, ES6

require()Functions are loaded asynchronously when loading dependent functions, which is also an example I put heresetTimeoutConfirm that the browser will not lose response. The callback function specified by the browser will not run until the previous modules are loaded successfully, which solves the dependency problem. AMD’s asynchronous loading solves the problems of blocking loading and performance, and the dependency between modules can be clearly displayed.

CMD (Common Module Definition)

CMD isSeaJSIn the process of promotion, the standardized output of module definition. The standard of CMD is very similar to AMD, but the operation mechanism is different to solve the same problem. For the dependent modules, CMD is highly recommendedDelay execution

//A.js (define modules without dependencies)
define(function (require, exports, module) {
    let data = 'aaa'
    function doSomething () {
        console.log(data)
    }
    exports.doSomething  =Dosomething // expose interface
})
//B.js (define dependent modules)
define(function (require, exports, module) {
    let data = 'bbb'
    function doSomething () {
        Var C = require ('. / C') // dependencies can be written nearby  
        console.log(data + c.data)
    }
    exports.doSomething  =Dosomething // expose interface
})
//C.js (this module is B.js dependent)
define(function (require, exports, module) {
    let data = 'ccc'
    exports.data  =Data // exposure module
})
//Introduce dependent modules
define(function (require, exports, module) {
  //Introduction of dependent modules (asynchronous)
  require.async('./a', function (a) {
    a.doSomething()
    console.log ('a is asynchronous')
  })
  //Introducing dependent modules (synchronization)
  Var B = require ('. / B') // dependency can be written nearby  
  b.doSomething()
  // ... 
  Var C = require ('. / C') // dependencies can be written nearby  
  console.log(c.data)
  // ...
})
<body>
    <script></script>
    <script>
        setTimeout(() => {
            console.log('setTimeout')
        }, 0)
        seajs.use('./index')
    </script>
</body>

Modularization - commonjs, AMD, CMD, ES6

ES6

The design idea of ES6 module is to be static as much as possible, so that the dependence of the module and the input and output variables can be determined at compile time. Commonjs and AMD modules can only determine these things at runtime.

exportThe command is used to specify the external interface of the module,importCommands are used to enter functions provided by other modules. In order to provide convenience, you can load the module without reading the documentexport defaultCommand to specify the default output for the module.

//A.js (definition module)
var data = 'aaa'
var doSomething = function () {
  console.log('log: ' + data)
};
export { data, doSomething }

//Reference module 
import { data, doSomething } from './a'

Here, I don’t introduce the grammar too much, but mainly talk about itES6 moduleAndCommonjs moduleThe difference between them.

There are two major differences:

  • The commonjs module outputs a copy of a value, while the ES6 module outputs a reference to a value.
  • The commonjs module is loaded at run time, and the ES6 module is the output interface at compile time.

The second difference is that commonjs loads an object (i.e module.exports The object is generated only after the script has run. The ES6 module is not an object, its external interface is just a static definition, which will be generated in the static parsing phase of the code.

Let’s take a look at the first difference, the loading mechanism of the commonjs module

// module1.js
var data = 5;
var doSomething = function () {
  data++;
};
//Exposed interface
module.exports.data = data;
module.exports.doSomething = doSomething;
var example = require('./module1.js');
console.log(example.data); // 5
example.doSomething(); 
console.log(example.data); // 5

Loading mechanism of ES6 module:

// module1.js
let data = 5;
function doSomething() {
  data++;
}
export { data, doSomething }
import { data, doSomething } from './module1';
console.log(data); // 5
doSomething();
console.log(data); // 6

The operation mechanism of ES6 module is different from that of commonjs.ES6 module is a dynamic reference and does not cache values. Variables in the module are bound to the module in which they are located.

summary

  • The output of the commonjs module is a copy of a value, and the commonjs module is loaded at runtimeCommonjs specification is mainly used for server programming,The loading module is synchronizedSynchronous means blocking loading, browser resources are loaded asynchronously, so AMD and CMD solutions are available.
  • AMD isRequireJSIn the process of promotion, the standardized output of module definition. Application of AMD specification in browser environmentAsynchronous loading moduleAnd multiple modules can be loaded in parallel. AMD’s API isOne for manyFor dependent modules,Amd advocates early execution (relying on front end)
  • CMD isSeaJSIn the process of promotion, the standardized output of module definition. The API of CMD is strictly distinguished and highly respectedSingle responsibilityThe loading module is asynchronous, and CMD advocates delayed execution.
  • The output of ES6 module is the reference of value, and ES6 module is the compile time output interfaceES6 realizes the module function on the level of language standard, which is simple and can be a general module solution for browser and server.