What is JS modularity? Why modularity?

When JS appeared, JS was generally used to implement some simple interactions, but later JS began to gain attention to implement more and more complex functions.

For the convenience of maintenance, we also extract THE JS of different functions as a JS file, but when the project becomes complex, an HTML page may need to load several JS files, and this time there will be various naming conflicts.

If JS can also put files of different functions into different packages like Java, and import related packages when a function or function needs to be referenced, various problems such as naming conflicts can be solved well. However, without the concept of modules in JS, how can modularization be realized?

Modular development is a kind of management style, is a kind of mode of production, a kind of solution, a module file, for the achievement of a specific function module, we can more easily use other people’s code, to want what function, what is loading module, but module development needs to follow certain specification, otherwise it will become jammed up, therefore, Then came the FAMILIAR AMD specification, CMD specification.

In a nutshell, it’s about managing variables

Front-end modular: CommonJS,AMD,CMD,ES6

Modularized development can improve code reuse rate and facilitate code management. Usually a file is a module with its own scope, exposing only specific variables and functions. Popular JS modular specifications include CommonJS, AMD, CMD and ES6 module systems. See Teacher Ruan Yifeng’s article module-loader.

First, the CommonJS

Node.js is a major practitioner of the commonJS specification and has four important environment variables to support modular implementations: Module, exports, require, global. Module. exports defines the interface for the current module’s exports (not recommended), and requires loads the module.

// Define the module math.js
var basicNum = 0;
function add(a, b) {
  return a + b;
}
module.exports = { // Write functions and variables that need to be exposed here
  add: add,
  basicNum: basicNum
}

// When referencing a custom module, the argument contains the path and can omit.js
var math = require('./math');
math.add(2.5);

// No path is required when referencing core modules
var http = require('http'); http.createService(...) .listen(3000);
Copy the code

CommonJS loads modules synchronously. On the server side, the module files are stored on local disk and are very fast to read, so this should not be a problem. However, on the browser side, it is more reasonable to use asynchronous loading for network reasons.

Exports and the module exports

Module.exports is preferred

For convenience, Node provides an exports variable for each module, pointing to module.exports. This equates to a line of command in the header of each module.

var exports = module.exports;
Copy the code

So we can add methods directly to exports objects to represent interfaces that export, just like we did on Module.exports.

Module. exports: Node exports from module. Exports: Node exports from module. Exports: Node exports from module.

// a.js
exports = function a() {};

// b.js
const a = require('./a.js') A is an empty object
Copy the code

Reference the difference – between – the module – exports – and – exports

AMD and require.js

The AMD specification uses asynchronous loading of modules, which does not affect the execution of subsequent statements. All statements that depend on this module are defined in a callback function that will not run until the load is complete. Here is an introduction to implement the modularization of the AMD specification with require.js: specify the reference path with require.config(), define the module with define(), and load the module with require().

First we need to import the require.js file and an entry file main.js. Configure require.config() in main.js and specify the base module to use in the project.

/** 网页中引入require.js及main.js **/
<script src="js/require.js" data-main="js/main"></script>

/** main.js entry file/main module **/
// First specify the module path and reference name with config()
require.config({
  baseUrl: "js/lib".paths: {
    "jquery": "jquery.min".// The actual path is js/lib/jquery.min.js
    "underscore": "underscore.min",}});// Perform basic operations
require(["jquery"."underscore"].function($, _){
  // some code here
});
Copy the code

When referencing modules, we place the module name in [] as the first argument to reqiure(); If we define modules that themselves depend on other modules, we need to place them in [] as the first argument to define().

// Define the math.js module
define(function () {
    var basicNum = 0;
    var add = function (x, y) {
        return x + y;
    };
    return {
        add: add,
        basicNum :basicNum
    };
});
// Define a module that depends on glo.js
define(['underscore'].function(_){
  var classify = function(list){
    _.countBy(list,function(num){
      return num > 30 ? 'old' : 'young'; })};return {
    classify :classify
  };
})

// Reference the module, put the module in []
require(['jquery'.'math'].function($, math){
  var sum = math.add(10.20);
  $("#sum").html(sum);
});
Copy the code

CMD and sea-.js

Require.js loads and executes the code in the module when it declares a dependent module:

define(["a"."b"."c"."d"."e"."f"].function(a, b, c, d, e, f) { 
    // all modules are declared and initialized first
    if (false) {
      // Module B is executed ahead of time even if it is not used
      b.foo()
    } 
});
Copy the code

CMD is another JS modularization solution, which is similar to AMD, except that AMD advocates relying on front-loading and up-front execution, while CMD advocates relying on nearby and delayed execution. This specification was actually created during the promotion of Sea-js.

/** AMD **/
define(["a"."b"."c"."d"."e"."f"].function(a, b, c, d, e, f) { 
     // all modules are declared and initialized first
    a.doSomething();
    if (false) {
        // Module B is executed ahead of time even if it is not used
        b.doSomething()
    } 
});

/** CMD **/
define(function(require.exports.module) {
    var a = require('./a'); // If required
    a.doSomething();
    if (false) {
        var b = require('./b'); b.doSomething(); }});/** sea.js **/
// Define the module math.js
define(function(require.exports.module) {
    var$=require('jquery.js');
    var add = function(a,b){
        return a+b;
    }
    exports.add = add;
});
// Load the module
seajs.use(['math.js'].function(math){
    var sum = math.add(1+2);
});
Copy the code

Fourth, the ES6 Module

ES6, on the level of language standards, implements module functions, and implements them quite simply, aiming to become a common module solution for browsers and servers. Its module functions are mainly composed of two commands: export and import. The export command is used to specify the external interface of a module, and the import command is used to input functions provided by other modules.

/** Define the module math.js **/
var basicNum = 0;
var add = function (a, b) {
    return a + b;
};
export { basicNum, add };

/** references the module **/
import { basicNum, add } from './math';
function test(ele) {
    ele.textContent = add(99 + basicNum);
}
Copy the code

As shown in the example above, when using the import command, the user needs to know the name of the variable or function to be loaded. In fact, ES6 also provides the export default command, which specifies the default output of the module. The corresponding import statement does not need curly braces. This is also closer to ADM’s citation style.

/** export default **/
// Define the output
export default { basicNum, add };
/ / introduction
import math from './math';
function test(ele) {
    ele.textContent = math.add(99 + math.basicNum);
}
Copy the code

ES6 modules are not objects, and the import command is statically analyzed by the JavaScript engine. The module code is introduced at compile time, rather than loaded at run time, so conditional loading cannot be implemented. Because of this, static analysis is possible.

5. Differences between ES6 module and CommonJS module

1. The CommonJS module outputs a copy of a value, while the ES6 module outputs a reference to a value.

  • The CommonJS module outputs a copy of the value, meaning that once a value is output, changes within the module do not affect that value.
  • ES6 modules operate differently from CommonJS. The JS engine encountered a module load command while statically analyzing the scriptimport, a read-only reference is generated. When the script is actually executed, it will be evaluated in the loaded module based on the read-only reference. In other words, ES6importIt’s kind of like a symbolic link on Unix, where the original value changes,importThe loaded value will also change. Therefore, ES6 modules are referenced dynamically and do not cache values. Variables in modules are bound to the module in which they are located.

2. CommonJS module is run time loading, ES6 module is compile time output interface.

  • Runtime loading: CommonJS modules are objects; That is, the entire module is loaded on input, an object is generated, and methods are read from that object. This loading is called “runtime loading.”
  • Load at compile time: ES6 modules are not objects, but passexportThe command explicitly specifies the code to output,importIs in the form of a static command. In theimportYou can specify that an output value is loaded instead of the entire module, which is called “compile-time loading.”

CommonJS loads an object (that is, the module.exports property) that is generated only after the script runs. An ES6 module is not an object, and its external interface is a static definition that is generated during the code static parsing phase.

Refer to the article

Author: subwaydown

Links: juejin. Cn/post / 684490…

Source: Nuggets