Front end modularization

Time:2021-7-16

First, modular understanding.

1. What is module?

  • A complex program is packaged into several blocks (files) according to certain rules (specifications) and combined together

  • The internal data and implementation of the block are private, but some interfaces (Methods) are exposed to communicate with other external modules

2. The evolution of modularity

(1) Global function mode: encapsulate different functions into different global functions

  • Coding: encapsulating different functions into different global functions

  • Disadvantages: polluting the global namespace, easy to cause naming conflict or data insecurity, and there is no direct relationship between module members

function m1(){
    //...
}
function m2(){
    //...
}

(2) Namespace pattern: simple object encapsulation

  • Advantages: reduce global variables and solve naming conflicts

  • Disadvantages: data is not secure (external data can be directly modified inside the module)

let myModule = {
    data: 'www.baidu.com',
    foo() {
        console.log(`foo() ${this.data}`)
    },
    bar() {
        console.log(`bar() ${this.data}`)
    }
}
Mymodule. Data ='Other data '// can directly modify the internal data of the module
myModule.foo() // foo() other data

(3) Iife mode: anonymous function self call (closure)

  • Advantages: the data is private and can only be operated by exposed methods

  • Coding: encapsulate the data and behavior into a function, and expose the interface by adding properties to the window

  • Disadvantage: what if the current module depends on another module?

//Index.html file


    myModule.foo()
    myModule.bar()
    Console.log (mymodule. Data) // undefined cannot access the internal data of the module
    Mymodule. Data ='xxxx '// not the data inside the modified module
    Mymodule. Foo() // no change
//Module.js file
(function(window) {
    let data = 'www.baidu.com'
    //Functions that manipulate data
    function foo() {
        //Used to expose existing functions
        console.log('foo() ${data}')
    }
    function bar() {
        //Used to expose existing functions
        console.log('bar() ${data}')
        Otherfun() // internal call
    }
    function otherFun() {
        //Internally private functions
        console.log('otherFun()')
    }
    //Exposure behavior
    Window. Mymodule = {foo, bar} // ES6
})(window)

The final result is obtained

foo() www.baidu.com
bar() www.baidu.com
otherFun()
undefined
foo() www.baidu.com

(4) Iife pattern enhancement: introducing dependency

This is the cornerstone of modern module implementation

//Index.html file




    myModule.foo()
//Module.js file
(function(window, $) {
    let data = 'www.baidu.com'
    //Functions that manipulate data
    function foo() {
        //Used to expose existing functions
        console.log('foo() ${data}')
        $('body').css('background', 'red')
    }
    function bar() {
        //Used to expose existing functions
        console.log('bar() ${data}')
        Otherfun() // internal call
    }
    function otherFun() {
        //Internally private functions
        console.log('otherFun()')
    }
    //Exposure behavior
    Window. Mymodule = {foo, bar} // ES6
})(window, jQuery)

In the example above, the background color of the page is changed to red through the jQuery method, so the jQuery library must be introduced first, and then it is passed in as a parameter. This not only ensures the independence of modules, but also makes the dependency between modules obvious.

3. Benefits of modularity

  • Avoid naming conflicts (reduce namespace pollution)

  • Better separation and on-demand loading

  • Higher reusability

  • high maintainability

4. Problems occurred after introducing multiple scripts

  • Too many requests

First of all, we have to rely on multiple modules, which will send multiple requests, resulting in too many requests

  • Dependency ambiguity

We don’t know what their specific dependencies are, that is to say, it’s easy to make mistakes in loading sequence because we don’t understand their dependencies.

  • Difficult to maintain

The above two reasons lead to difficult maintenance, which may lead to serious problems in the project.
Modularization has many advantages, but a page needs to introduce multiple JS files, which will lead to the above problems. And these problems can be solved by modular specification, the most popular specifications are commonjs, AMD, ES6 and CMD.

Two, modular specification

1. CommonJS

(1) Overview

The node application is composed of modules and adopts the common JS module specification. Each file is a module with its own scope. Variables, functions and classes defined in one file are private and invisible to other files. On the server side, modules are loaded synchronously at runtime; On the browser side, modules need to be compiled and packaged in advance.

(2) Characteristics

  • All code runs in the module scope and does not pollute the global scope.

  • The module can be loaded many times, but it will only be run once when it is loaded for the first time, and then the running results will be cached. When it is loaded later, the cached results will be read directly. For the module to run again, the cache must be cleared.

  • Modules are loaded in the order they appear in the code.

(3) Basic grammar

  • Exposure module: module. Exports = value or exports. XXX = value

  • Import module: require (xxx). If it is a third-party module, XXX is the module name; If it is a custom module, XXX is the module file path

// example.js
var x = 5;
var addX = function (value) {
    return value + x;
};
module.exports.x = x;
module.exports.addX = addX;
var example = require('./example.js');// If the parameter string starts with a "." / ", it means that a relative path is loaded
console.log(example.x); // 5
console.log(example.addX(1)); // 6

The require command is used to load the module file. The basic function of the require command is to read in and execute a JavaScript file, and then return the exports object of the module. If the specified module is not found, an error will be reported.

(4) Module loading mechanism

The loading mechanism of the commonjs module is that the input is a copy of the output value. In other words, once a value is output, the internal changes of the module will not affect the value. This is very different from ES6 modularization

// lib.js
var counter = 3;
function incCounter() {
    counter++;
}
module.exports = {
    counter: counter,
    incCounter: incCounter,
};
// main.js
var counter = require('./lib').counter;
var incCounter = require('./lib').incCounter;

console.log(counter);  // 3
incCounter();
console.log(counter); // 3

The above code shows that after counter output, the internal changes of lib.js module will not affect counter. This is because counter is a value of the original type and will be cached. Unless it is written as a function, we can get the value after internal change.

2. AMD

The loading module of commonjs specification is synchronous, that is to say, only after the loading is completed can the following operations be performed. Amd specification is an asynchronous loading module, which allows to specify callback function. Because node.js is mainly used for server programming, module files generally exist in the local hard disk, so it is relatively fast to load, and there is no need to consider the asynchronous loading method, so the commonjs specification is more applicable. However, if it is a browser environment, to load the module from the server side, asynchronous mode must be adopted. Therefore, the browser side generally adopts amd specification, and requirejs follows amd specification. In addition, amd specification is earlier than commonjs specification in browser implementation.

Define exposure module:

//Define modules that have no dependencies
define(function(){
    Return module
})
//Define dependent modules
define(['module1', 'module2'], function(m1, m2){
    Return module
})

Introduce the use module:

require(['module1', 'module2'], function(m1, m2){
    Use M1 / m2
})

Define and require are two methods to define and call modules, which are collectively called amd mode. Its module definition method is very clear, does not pollute the global environment, and can clearly display the dependency relationship.
Amd mode can be used in browser environment, and can load modules asynchronously or dynamically as needed.

3. CMD

The CMD specification is specially used for the browser side, the module loading is asynchronous, and the module will be loaded and executed when it is used. The CMD specification integrates the characteristics of commonjs and AMD specifications. In sea. JS, all JavaScript modules follow the CMD module definition specification.
Define exposure module:

//Define modules that have no dependencies
define(function(require, exports, module){
    exports.xxx = value
    module.exports = value
})
//Define dependent modules
define(function(require, exports, module){
    //Introducing dependent modules (synchronization)
    var module2 = require('./module2')
    //Introduction of dependent modules (asynchronous)
    require.async('./module3', function (m3) {
    })
    //Exposure module
    exports.xxx = value
})

Introduce the use module:

define(function (require) {
    var m1 = require('./module1')
    var m4 = require('./module4')
    m1.show()
    m4.show()
})

4. ES6 modularization

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. For example, the commonjs module is an object, and you must look up the object properties when you input.

(1) ES6 modular syntax

The export command is used to specify the external interface of the module, and the Import command is used to input the functions provided by other modules.

/**Define module math.js**/
var basicNum = 0;
var add = function (a, b) {
    return a + b;
};
export { basicNum, add };
/**Reference module**/
import { basicNum, add } from './math';
function test(ele) {
    ele.textContent = add(99 + basicNum);
}

When using the Import command, the user needs to know the name of the variable or function to be loaded, otherwise it cannot be loaded. In order to provide convenience for users, so that they can load the module without reading the document, we need to use the export default command to specify the default output for the module.

// export-default.js
export default function () {
    console.log('foo');
}
// import-default.js
import customName from './export-default';
customName(); // 'foo'

When other modules load the module, the Import command can specify any name for the anonymous function.

(2) The difference between require and import

  • Require is the syntax of commonjs (AMD specification is introduced). Commonjs module is the object
    Import is the syntax of ES6, and the ES6 module is not an object
  • Requrie is a runtime load (generates an object and takes values from it)
    Import is loaded at compile time (after confirming the reference relationship, the import variable will be promoted: column 1)
  • Require assignment or shallow copy (basic type is copy, object type is shallow copy)
    Import is similar to deconstructing assignment (I call whoever I need)

(3) Differences between ES6 module and commonjs module

① The output of the commonjs module is a copy of the value, so when the value changes in the module, the output value will not be affected. ES6
The module outputs a reference to a value.
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.

② The commonjs module is loaded at run time, and the ES6 module is the output interface at compile time.
Commonjs loads an object (that is, the module. Exports attribute), which is generated only after the script is 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.

3、 Summary

  • Both commonjs and AMD are run-time loads

  • Commonjs specification is mainly used for server programming, and the loading module is synchronous, which is not suitable for browser environment, because synchronization means blocking loading, and browser resources are loaded asynchronously, so amd CMD solution is available.

  • Amd standard loads modules asynchronously in browser environment, and can load multiple modules in parallel. However, the cost of AMD specification development is high, it is difficult to read and write code, and the semantics of module definition is not smooth.

  • The CMD specification is very similar to the AMD specification. Both of them are used for browser programming. They are dependent on the nearby and can be executed late. They can be easily run in node.js. However, depending on SPM packaging, the module loading logic is biased

  • ES6 realizes the module function on the level of language standard, and it is quite simple. It can completely replace the common JS and AMD specifications and become a common module solution for browsers and servers.

The main purpose of mark is to sort out the records. If there are any mistakes, please point them out