preface

What is modularity? What does it do? Why learn modularity for a front-end newbie? I believe that such a question will certainly appear in many white minds. Some people may be like, don’t learn how to pass the interview ah, don’t learn where to find a job ah, such an idea is wrong, don’t learn for the sake of learning, now that we have decided to pursue the career of front-end engineer, then we must in order to become a qualified front-end and strive. This is true not only for learning front-end modularity, but also for learning other things.

The preexistence of modularity

Why modularity?

You would have been ridiculed if you told the front-end engineer ten or even three years ago that the project needed to be modular, because the front-end engineer’s tasks back then were not as complex as they are today, which is what JS is all about, which is to implement simple page interaction logic. With the development of science and technology and the continuous iteration of technology update. The performance of the browser has been greatly improved, the processing power of the COMPUTER CPU is becoming stronger and stronger, what’s more, the emergence of endless front-end libraries and frameworks makes the front-end code is expanding. At this point, the front-end engineer has to figure out how to organize the code, or the whole program’s front-end code will look messy, let alone maintainable.

What is modularity?

  • Modularization is to package a complex program into several blocks according to certain rules and combine them together.
  • The internal data and implementation of the block are private, exposing only interfaces to communicate with external modules.

Modular past life

Global function pattern: Encapsulates different functions into different global functions

  • The earliest prototype of modularity was to define functions globally to implement different functions
  • Disadvantages:
    • Contaminate global variables and cause variable conflicts easily
    • Data is vulnerable to modification and unsafe
    • There is no direct connection between the modules
function add(){
  //...
}
function delete(){
  //...
}
Copy the code

Namespace mode: Simple object encapsulation

Since the global function pattern will pollute global variables, I can manage them all in one object.

  • Advantages: It solves the problem of polluting global variables.
  • Disadvantages:
    • Data is still insecure and easily modified (external data can be directly modified internally)
let myModule = { data: 'www.baidu.com', foo() { console.log(`foo() ${this.data}`) }, Bar () {console.log(' bar() ${this.data} ')}} mymodule.data = 'other data' mymodule.foo () // foo() other dataCopy the code

Writing this way exposes all module members, and the internal state can be overwritten externally.

IIFE mode: Anonymous function self-invocation (closure)

I guess you actually think of closures when you see the definition of modularity. Yes, to prevent the internal state from being easily overwritten, use closures to implement code encapsulation.

  • Advantages: data privatization, external operations can only be carried out through exposed methods
  • Disadvantages:
    • This makes it impossible for outsiders to modify internal data, but it also fails to show how modules relate to each other.
// index.html file <script type="text/javascript" SRC ="module.js"></script> <script type="text/javascript"> mymodule.foo () Mymodule.bar () console.log(mymodule.data) //undefined access to internal data mymodule. data = 'XXXX' // Not modified internal data mymodule.foo () // No changes to </script>Copy the code
// module.js file (function(window) {let data = 'www.baidu.com' // function foo() {console.log(' foo()) ${data} ')} function bar() {console.log(' bar() ${data} ') otherFun() {// Internal call} function otherFun() {// internal private function Console. log('otherFun()')} // Expose behavior window.myModule = {foo, bar} //ES6})(window)Copy the code

The final result:

IIFE schema enhancement: Introduce dependencies

The cornerstone of modern modularity, the solution to the problem of previously not being able to relate modules to each other, is to introduce dependencies.

// module.js file (function(window, $) {let data = 'www.baidu.com' // function foo() {// Used to expose function console.log(' foo() ${data} ') $('body').css('background', 'red')} function bar() {// Used to expose console.log(' bar() ${data} ') otherFun() // internal call} function otherFun() {// internal private function Console. log('otherFun()')} // Expose behavior window.myModule = {foo, bar}})(window, jQuery)Copy the code
// index.html <! <script type="text/javascript" SRC ="jquery-1.10.1.js"></script> <script type="text/javascript" src="module.js"></script> <script type="text/javascript"> myModule.foo() </script>Copy the code

The previous example changed the background color of the page to red using jquery, so you must import the jquery library as a parameter. In addition to ensuring module independence, this makes dependencies between modules obvious.

Current modularity benefits

Modularity has the following benefits as can be seen from the development history above

  • Avoid naming conflicts
  • Better separation, load on demand
  • Higher reusability
  • Better maintainability

Disadvantages of current modularity

As you can see, modularity now requires the introduction of multiple external dependencies through Script. Obviously this can be problematic as the project gets bigger.

  • Request too much

First we rely on multiple modules, which will send multiple requests, resulting in too many requests

  • Relying on the fuzzy

We don’t know what their dependencies are, which means it’s easy to get the load sequencing wrong because you don’t know what their dependencies are.

  • Difficult to maintain

The above two reasons lead to difficult maintenance, and it is likely to affect the whole situation, leading to serious problems in the project. Modularity certainly has several benefits, however, a page needs to introduce multiple JS files, will occur these problems. These issues can be addressed through modular specifications. The commonJS, AMD, ES6, CMD specifications that are most popular in development are described below.

Ii. Modularity in this life

Front-end modularity is now generally achieved through some kind of modularity specification. The best-known front-end modular specifications are CommonJS,AMD,CMD and ES6, modular.

We continue to interpret these modularity specifications in a hierarchical manner.

CommonJS

I believe those of you who have come into contact with Node must know that the modularity of Node applications is the CommonJS specification. The same goes for WebPack.

An overview of the

In the CommonJS specification.

  • A 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.

The characteristics of

  • 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 when they are loaded later. For the module to run again, the cache must be cleared.
  • The order in which modules are loaded, in the order they appear in the code.

The 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

Knowing the “is” requires knowing the “why”. Then what exactly do module and export represent?

CommonJS internally states that each module’s internal module represents the current variable, which is the current object. Its exports are the attributes of the current variable that represent the exposed interface. Loading a module loads the module.exports property of that module.

// example.js
var x = 5;
var addX = function (value) {
  return value + x;
};
module.exports.x = x;
module.exports.addX = addX;
Copy the code

The code above exports the x variable and the function addX via module.exports.

var example = require('./example.js'); // If the argument string starts with a "./ ", a relative path console.log(example.x) is loaded; // 5 console.log(example.addX(1)); / / 6Copy the code

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

Module loading mechanism

In the CommonJS specification, the input value is a copy of the output value. That is, once a value is entered, changes within the module no longer affect that value.

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.

AMD

CommonJS loads synchronously, meaning that no further operations can be performed until the load is complete. The AMD specification loads modules asynchronously and allows you to specify callback functions.

On the server side, node.js is mainly used for server programming, and 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 mode, so the CommonJS specification is suitable.

However, in the browser environment, to load modules from the server side, then must be in asynchronous mode, so the browser side generally uses the AMD specification. In addition, the AMD specification was implemented on the browser side much earlier than the CommonJS specification.

AMD specification basic syntax

Define exposure modules:

Define (function(){return module})Copy the code
// Define dependent modules (['module1', 'module2'], function(m1, m2){return module})Copy the code

Introducing usage modules:

Require (['module1', 'module2'], function(m1, m2){use m1/m2})Copy the code

CMD

The CMD specification is designed specifically for the browser side. Modules are loaded asynchronously and executed only when they are in use. The CMD specification incorporates the features of the CommonJS and AMD specifications. In Sea-.js, all JavaScript modules follow the CMD module definition specification.

CMD regulates the basic syntax

Define exposure modules:

Define (function(require, exports, module){exports. XXX = value module.exports = value})Copy the code
// define(function(require, exports,) Var module2 = require('./module2') // 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

ES6 modular

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.

ES6 modular syntax

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.

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); }Copy the code

As shown in the preceding example, when using the import command, the user needs to know the name of the variable or function to be loaded; otherwise, the command cannot be loaded. To make it easier for users to load the module without reading the documentation, use the export default command to specify the default output for the module.

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

The import command can specify any name for the anonymous function when the module is loaded by other modules.

Differences between ES6 modules and CommonJS modules

There are two important differences:

The CommonJS module outputs a copy of a value. The ES6 module outputs a reference to a value.

② CommonJS module is loaded at runtime, ES6 module is output at compile time.

  • 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.”

  • Compile-time loading: ES6 modules are not objects, but are output code explicitly specified through the export command and static commands when importing. That is, when you import, you 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.

To explain the first difference, let’s use the CommonJS module loading mechanism as an example:

// lib.js export let counter = 3; export function incCounter() { counter++; } // main.js import { counter, incCounter } from './lib'; console.log(counter); // 3 incCounter(); console.log(counter); // 4 ' 'ES6 module works differently from CommonJS. **ES6 modules are referenced dynamically and do not cache values. Variables in modules are bound to their own modules **.Copy the code

3. Summary

  • 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.

reference

Front-end Modular Details (Full version)

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