First, what is modularity?

Modularity refers to the decomposition of a complex program into multiple modules for easy coding

Second, why use modularity?

2.1. Function writing

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

Copy the code

The above functions m1 and m2 are equivalent to a module. When used, they can be called directly.

However, the disadvantages of this approach are obvious: since the function is mounted directly under the Window object, it “taints” the global variables, is not guaranteed to avoid variable name conflicts with other modules, and there is no direct relationship between module members.

2.2 Object writing method

Since the window object has only so many namable attribute names, I declare an object on top of the Window (global) object, and put all module members into this object.

var module = {
    count: 0,
    function m1(){
        // xxx
    }
    function m2(){
        // xxx
    }
}

Copy the code

The above functions m1() and m2() are encapsulated in the Module1 object. When used, it calls a property of the object.

module.m1();
Copy the code

However, this way of writing exposes all module members, and the internal state can be overwritten externally. For example, external code can directly change the value of an internal counter.

module1._count = 5;
Copy the code

2.3. Execute the function immediately

To prevent internal members from being exposed, we can privatize variables with immediate execution functions.

const module2 = (function() {
	let _money = 100
	const m1 = () => {
		console.log(123)
	}
	const m2 = () => {
		console.log(456)
	}
	return {
		f1: m1,
		f2: m2
	}
})()
Copy the code

External code cannot read the internal _count variable using the above notation.

console.info(module2._count); //undefined
Copy the code

However, the internal variables of function are hidden from the whole world, thus achieving the purpose of encapsulation. However, the Module2 variable is still exposed globally, and the number of global variables will continue to increase as more modules are added.

2.4. Use Script to reference JS modules

<script type="text/javascript" src="a.js"></script>
<script type="text/javascript" src="b.js"></script>
<script type="text/javascript" src="c.js"></script>
<script type="text/javascript" src="d.js"></script>
Copy the code

Disadvantages:

(1) When loading, it will stop rendering the web page. The more JS files are introduced, the longer the web page will lose response;

(2) it will pollute global variables;

(3) There is a dependency relationship between JS files, loading is sequential, the most dependent should be put to the last load; Dependencies become complex when the project is large.

(4) There are too many JS files to introduce, which are not beautiful and difficult to manage the code.

2.5,

Using function writing leads to global variable contamination and can lead to naming conflicts

Using namespaces causes internal attributes to be exposed and can cause internal members to be overwritten

The use of immediate functions to privatize variables can achieve some defensive effect. Is one of the better early modular solutions

Using Script to reference JS modules leads to complex file relationships that are difficult to manage

Three, modular specification

3.1, CommonJS

In 2009, American programmer Ryan Dahl created the Node.js project to use the javascript language for server-side programming. This marks the official birth of “Javascript modular programming “. Because honestly, in a browser environment, the lack of modules isn’t that much of a problem, given the limited complexity of web applications; But on the server side, you have to have modules that interact with the operating system and other applications, or you can’t program at all. The module system of Node.js is realized by referring to CommonJS specification.

3.1.1 CommonJS features

  • All code runs in the module scope and does not pollute the global scope.
  • Modules can be loaded multiple times, but only run once on the first load, and then the results are cached and read directly from the cache. For the module to run again, the cache must be cleared
  • Modules are loaded synchronously, so modules are loaded in the order they appear in the code
  • CommonJS uses synchronous loading of different module files and is suitable for the server side. Because module files are stored in each hard disk of the server, the load time is fast, suitable for the server side, not suitable for the browser. The browser is not compatible with CommonJs due to the lack of module, exports, require and global environment variables. Tool conversion is required for use.

3.1.2 basic Grammar

  • Exposure module:module.exports = valueorexports.xxx = value
  • Introduction module:require(xxx)If the module is a third-party module, XXX is the module name. For a custom module, XXX is the module file path.

Here we have a question: What exactly is CommomJS exposing? The CommonJS specification states that within each module, the module variable represents the current module. This variable is an object whose exports property (module.exports) is the external interface. Loading a module loads the module.exports property of that module.

// a.js
module.exports = {
    a: 1
}
// or 
exports.a = 1

// b.js
var module = require('./a.js')
module.a // -> log 1
Copy the code

Module. exports and exports? Why are these lines of code modularized? Let’s take a look at the basic implementation

Let’s say the require

var module = require('./a.js') module. / / here is actually a packaging with a layer of immediate execution function, global variables, so no pollution / / here it is important to the module, the module is unique to the Node of a variable module. Exports = {a: Var module = {id:'xxxx'Exports: I knew where to find him. Exports var load = module. Exports var load = module. Exports var load = modulefunction(module) {var a = 1 module.exports = areturnmodule.exports }; // Then when I require to find the unique // id, then wrap the thing to be used with the immediate execution function, overCopy the code

Module. exports and exports.

  1. exportsIs pointing tomodule.exportsA reference to the
  2. module.exportsThe initial value is an empty object {}, soexportsThe initial value is also {}, but not correctexportsIt doesn’t have any effect, and those of you who have seen the code above can see why.
  3. require()Returns themodule.exportsRather thanexports

3.1.3 Loading mechanism of modules

The loading mechanism for CommonJS modules is that the input is a copy of the output value. That is, once a value is printed, changes within the module do not affect that value. This is a major difference from ES6 modularity (described below), as shown in the following example:

// lib.js
var counter = 3;
function incCounter() {
  counter++;
}
module.exports = {
  counter: counter,
  incCounter: incCounter,
};
Copy the code

The code above prints the internal variable counter and overwrites the internal method incCounter.

// main.js
var counter = require('./lib').counter;
var incCounter = require('./lib').incCounter; console.log(counter); // 3 incCounter(); console.log(counter); / / 3Copy the code

As the code above shows, once counter is printed, changes within the lib.js module do not affect counter. This is because counter is a primitive value that will be cached. You have to write it as a function to get the internal value.

The CommonJS specification is unique to Node and Browserify parsing is required if the browser wants to use it.

3.1.4, Browserify

Browserify lets you organize browser-side Javascript code in a way similar to Node’s require(), precompiling the front-end Javascript directly using some of the libraries installed by Node NPM. — From Baidu Encyclopedia

1) download

  • NPM install browserify -g
  • NPM install browserify –save-dev

Run browserify app.js -o bundle. JS to compile the app.js file output into the bundle. JS file

3.1.5,

  1. CommonJS uses synchronous loading of different module files. It works on the server side but not in a browser environment where synchronization means blocking loading and browser resources are loaded asynchronously
  2. Code cannot run directly in a browser environment and must be converted to standard ES5 using tools.

3.11, Asynchronous Module Definition (AMD)

This is the asynchronous module definition. As mentioned above, CommonJS is a specification for server-side modules, designed primarily for the back-end performance of JS, which is not well suited for the front-end. AMD is trying to standardize the performance of front-end JS. Because it is not native to JavaScript, page development using the AMD specification requires the corresponding library function, require.js (there is also a JS library: curl.js). In fact AMD is the result of the normalization of module definitions in require.js during the promotion process. AMD uses asynchronous mode to load the module, the module load does not affect its subsequent statement run. All statements that depend on this module are defined in a callback function that will not run until the load is complete.

3.2.1. Definition and use of modules

// Define a module define('module'['dep'].function (dep) {
  returnexports; }); If this parameter is not provided, the default script name (excluding the extension name) // dependencies is an array of module names used by the current module. A module initializes a function or object to be executed. If it is a function, it should be executed only once. If it is an object, this object should be the output value of the module.Copy the code

Require.js also uses the require() statement to load the module, but unlike CommonJS, it requires two arguments

// Import and use the module require([module], callback); // The second argument [module] is an array whose members are the modules to be loaded; // This callback will be called only after the previous module has been loaded. // The loaded modules are passed to the function as arguments so that they can be used inside the callback functionCopy the code

3.2.2. Take an example

// demo.html <body> // import the dependent file and main entry file <script SRC ="./require.js" data-main = './demo.js'></script>
</body>

// modules/m1.js
define(function(){
    var name = 'm1-amd';
    function getName() {return name;
    }
    return{getName} // exposed module}) // modules/m2.js // in m2 module, reference m1 module define(['m1'].function(m1){
    var msg = 'm2-amd';
    function show(){
        console.log(msg,m1.getName());
    }
    return{show} // exposed module}) //demo.js (function(){// configure the module path for each variable require.config({paths: {m1:'./modules/m1',
            m2: './modules/m2',
        }
    })
    require(['m2'].function(m2){ m2.show(); M2-amd m1-AMD})})()Copy the code

By default, require.js assumes that the loaded module is in the same directory as main.js and automatically loads it. If not in the same directory, we can use the require.config() method to customize the loading behavior of the module

It is also possible to reference third-party libraries in the above example with minor modifications to the base of the code above:

//demo.js
(function(){// configure the module path for each variable require.config({paths: {m1:'./modules/m1',
            m2: './modules/m2',
            jquery:'/ jquery - 3.3.1'
        }
    })
    require(['m2'.'jquery'].function(m2,$){ m2.show(); M2-amd m1-AMD $('body').css('backgroundColor'.'# 000');
    })
})()
Copy the code

Note that jquery has different specifications for modularity. For each module, there is an exposed interface name. For AMD, the exposed interface name is lowercase jquery, so you can’t write jquery in uppercase

/ / jquery 3.3.1. Jsif(typeof define === 'function' && define.amd){
    define('jquery'[],function() {returnjQuery; })}Copy the code

3.2.3 AMD Features:

Dependency preloading: A callback must wait until all dependent modules are loaded, even if the module is not used in the callback at all. It is now possible to load modules dynamically in AMD2.0

RequireJS pros and cons

Advantages:

1, suitable for asynchronous loading modules in the browser environment

2. Multiple modules can be loaded in parallel

Disadvantages:

1. It increases the development cost, makes it difficult to read and write the code, and the semantics of module definition are not smooth

2, does not conform to the general modular way of thinking, is a compromise to achieve

3.3, CMD (Common Module Definition)

The common module definition, corresponding to SeaJS, is the concept and design first proposed by Ali Yubo team. It addresses the same problem as requireJS, but runs differently.

3.3.1 The difference between CMD and AMD is that

(1)AMD advocates pre-reliance; CMD advocates relying on proximity, requiring only when a module is needed:

In layman’s terms:

AMD will execute the main logic only when it encounters require after all execution is complete. (Loaded in advance)

CMD will execute the corresponding module only when require is encountered. (Load on demand)

AMD user experience is good because there is no latency and CMD performance is good because it is executed only when the user needs it.

CMD comes into being because it’s nice to node.js writers, because it’s written in a way that makes Vue so popular.

3.3.2 CMD syntax

Sea-.js advocates one file per module, following a uniform writing method.

define(id? , deps? , factory)

Because CMD advocates one module per file, it often uses the filename as the module ID. CMD advocates nearby dependencies, so it usually doesn’t write dependencies in define. Factory has three parameters

function(require, exports, module)

  • Require is a method that takes a module representation as its only argument and is used to retrieve interfaces provided by other modules
  • Exports is an object that provides module interfaces
  • A module is an object that stores properties and methods associated with the current module

Defining exposure modules

// Define a module without dependencies (function(require, exports, module){
  exports.xxx = value
  module.exports = value
})

Copy the code
// Define dependent module define(function(require, exports, module){var module2 = require(exports, module)'./module2'// Introduce dependency modules (async) require.async('./module3'.function(m3) {}) // exports.xxx = value})Copy the code

Introducing usage modules

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

Copy the code

Sea-.js easy to use tutorial

① Download sea-.js and import it

Liverpoolfc.tv: seajs.org/

github : github.com/seajs/seajs

Then import sea-.js into the project: js/libs/ sea-.js

② Create a project structure

  |-modules
    |-m1.js
    |-m2.js
    |-m3.js
    |-m4.js
|-index.html
|-main.js
|-sea.js

Copy the code

③ Define the module code of sea-.js

// index.html
<body>
    <script src="./sea.js"></script> // introduce dependency file <script> seajs.use('./main.js'); </script> </body> // modules/m1.js define(function(require,exports,module){
    var msg = 'm1';
    function foo(){ console.log(msg); } module.exports = {// exposed interface foo:foo}}); // modules/m2.js define(function(require,exports,module){
    var msg = 'm2';
    function bar(){ console.log(msg); } module.exports = bar; / exposed interface}); // modules/m3.js define(function(require,exports,module){
    var msg = 'm3';
    function foo(){ console.log(msg); } exports. M3 = {foo:foo} / exposed interface}); // modules/m4.js define(function(require,exports,module){
    var msg = 'm4'; Var m2 = require('./m2'); m2(); // require. Async ()'./m3'.function(m3){
        m3.m3.foo();
    });
    function fun(){ console.log(msg); } exports.m4 = fun; }) //main.js define(function(require,exports,module){
    var m1 = require('./modules/m1');
    m1.foo();
    var m4 = require('./modules/m4');
    m4.m4();
})

Copy the code

The final result is as follows

3.3.3 advantages and disadvantages of CMD

Advantages: also realized the browser side of the modular loading. Can be loaded on demand, depending on the nearest.

Disadvantages: dependent on SPM packaging, heavy loading logic of modules

3.4, ES6 Module

The ES6 module is designed to be as static as possible, so that the module dependencies, as well as the input and output variables, can be determined at compile time. Both CommonJS and AMD modules can only determine these things at runtime. For example, a CommonJS module is an object, and you have to look for object properties when you enter it.

3.4.1 track of grammar

In ES6, the export keyword is used to export modules and the import keyword is used to reference modules. However, browsers are not fully compatible and need to use Babel to convert into ES5’s Require.

/ / exportexport function hello() {};exportdefault { // ... }; // import import {readFile } from 'fs';
import React from 'react';

Copy the code

When importing a module using import, you need to know the name of the variable or function to load.

Export Default is also provided in ES6, specifying the default output for the module. You do not need to use curly braces when importing the module import.

//math.js
var num = 0;
var add = function (a, b) {
  return a + b;
};
export{ num, add }; Import {num, add} from'./math';
function test(ele) {
  ele.textContent = add(1 + num);
}

Copy the code

3.4.2 Differences between ES6 and CommonJS

CommonJS

  • For basic data types, it is replication. It will be cached by the module. At the same time, in another module you can reassign the variable output from that module.

  • Shallow copy for complex data types. Because both modules reference objects that point to the same memory space, changes to the value of one module affect the other.

  • When a module is loaded using the require command, the entire module code is run.

  • When the same module is loaded using the require command, the module is not executed, but is fetched from the cache. That is, no matter how many times the CommonJS module is loaded, it only runs once on the first load and returns the result of the first run when it is loaded later, unless the system cache is manually cleared.

  • When cyclic loading is performed, it is load-time. When the script code is required, it will all be executed. Once a module is “looping”, only the parts that have been executed are printed, not the parts that have not been executed.

ES6 module

  • Values in ES6 modules are dynamic read-only references.

  • For read-only, that is, it is not allowed to change the value of the imported variable, and the imported variable is read-only, whether it is a basic data type or a complex data type. When a module encounters an import command, it generates a read-only reference. When the script is actually executed, it will be evaluated in the loaded module based on the read-only reference.

  • For dynamic, when the original value changes, so does the value that the import loads. Both basic and complex data types.

  • When looping, ES6 modules are referenced dynamically. As long as there is a reference between the two modules, the code can execute.

3.4.3 Advantages and disadvantages

Advantages:

1, easy to static analysis

2. Future-oriented EcmaScript standard

Disadvantages:

1. The browser is not fully compatible and must be converted to standard ES5 by tools to run properly.

2. New command words that are only supported by new versions of Node.js

Four,


  • The CommonJS specification is mainly used for server-side programming, loading modules are synchronous, which is not suitable in a browser environment because synchronization means blocking loading and browser resources are loaded asynchronously, hence the AMD CMD solution.

  • The AMD specification loads modules asynchronously in the browser environment, and multiple modules can be loaded in parallel. However, AMD specifications are expensive to develop, code is difficult to read and write, and semantics are not smooth in the way modules are defined.

  • The CMD specification is similar to the AMD specification in that it is used for browser programming, relies on proximity, executes lazily, and can be easily run in Node.js. However, depending on SPM packaging, the loading logic of modules is biased

  • ES6 in the language standard level, the realization of module function, and the implementation is quite simple, can completely replace CommonJS and AMD specifications, become a common browser and server module solution.