Introduction to front end modularization

Time:2022-5-11

1、 Modular introduction

1. Origin of modularization

  • problem

    -The problem of variable naming conflict caused by multi person cooperation
    -The code is chaotic and difficult to maintain

    Based on the emergence of the above problems, there is a modular solution.

  • result

    -Complex code can be divided into small modules to facilitate code management and maintenance
    -The direct content of each module is independent of each other

2 modular history

2.1 earliest modularization mode

1. Single case mode
If both developers have the same variable a, they can be distinguished in the following way.

var name1 = {
  a: 1
}
var name2 = {
  a: 2
}

However, this method does not completely solve the problem. After all, name1 and Name2 also need different names, and it is not very convenient to call this method
2 self executing function

function(){
  var a = 1
}()

function(){
  var a = 1
}()

Each function has its own scope, so there will be no conflict between the a variables in the above two functions. But this solution is not elegant

2.2 outdated modular mode

1 amd module specification
Amd – asynchronous module loading specification, that is, even if the required module has not been obtained during module loading, it will not affect the execution of the following code.
Requirejs — implementation of AMD specification. In fact, it can also be said that AMD is the standardized output of the module definition of requirejs in the promotion process.
Examples are as follows:

//Independent module definition
define({
  a: function() {}
  b: function() {}
}); 
//Definition of dependent modules
define(['f1', 'f2'], function(f1, f2){
  a: function() {}
  b: function() {}
});

//Module reference
require(['m1', 'm2'], function(m1, m2){
  m1.a();
  m2.b();
})

2 CMD module specification
CMD – general module specification, proposed by Yubo in China.
Seajs – the implementation of CMD. In fact, CMD can also be said to be the standardized output of module definition in the promotion process of seajs.
Usage example:

define(function(require, exports, module){
  //Dependent module a
  var a = require('./a');

  //Call the method of module a
  a.method();
})

The main difference from AMD specification lies in the definition of modules and the introduction of dependencies. Amd needs to specify all dependencies when declaring the module, and pass the dependencies to the module content through formal parameters. CMD module is closer to node’s definition of commonjs specification (which will be emphasized later). CMD supports dynamic introduction. Require, exports and module are passed to the module through formal parameters. When it needs to rely on the module, it can be introduced by calling require() at any time. Compared with AMD, CMD advocates relying on proximity, while amd advocates relying on front.

3 UMD general module specification
The so-called compatibility mode is to handle several common module definition methods in a compatible manner.

(function (global, factory) {
   typeof exports === 'object' && typeof module !== 'undefined' 
       ? module.exports = factory()          // Node , CommonJS
       : typeof define === 'function' && define.amd  
         ? define(factory)                   //AMD CMD
         : (global.CodeMirror = factory());  // Module mount to global
}(this, (function () { 
   ...
})

Next, we will introduce the most mainstream front-end modular scheme.

Module in node II

The node application is composed of modules and adopts the commonjs 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.

2.1 brief introduction to commonjs

The commonjs specification stipulates that within each module, the module variable represents the current module. This variable is an object, and its exports attribute (i.e. module. Exports) is an external interface. Loading a module is actually loading the module of the module Exports property.

//Referenced module file
var x = 5;
var addX = function (value) {
  return value + x;
};
module.exports.x = x;
module.exports.addX = addX;

//Load module file
var example = require('./example.js');

console.log(example.x); // 5
console.log(example.addX(1)); // 6

2.2 module object

2.2.1 module realization
Node provides a module build function internally. All modules are instances of module. Inside each module, there is a module object that represents the current module.

function Module(id, parent){
  this.id = id;
  this.exports = {};
  this.parent = parent;
  if(parent && parent.children) {
    parent.children.push(this);
  }
  this.filename = null;
  this.loaded = false;
  this.children = [];
}
  • module. Id the identifier of the module, usually the module file name with an absolute path.
  • module. Filename the file name of the module, with an absolute path.
  • module. Loaded returns a Boolean value indicating whether the module has completed loading.
  • module. Parent returns an object representing the module that calls the module.
  • module. Children returns an array indicating other modules to be used by the module.
  • module. Exports indicates the value of the module’s external output.

2.2.2 module. Exports and exports
module. In fact, the output of the module is the current output of the module Exports variable.
For convenience, node provides an exports variable for each module, pointing to module exports。 This is the same as the header of each module. There is one such command.
var exports = module.exports;
If you think, exports and module It is difficult to distinguish between exports. A simple way is to give up using exports and only use module exports。

2.3 require command

Node uses the commonjs module specification, and the built-in 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.

2.3.1 module classification in node
1. Core module / built-in module (FS, HTTP path, etc.)
2. The third-party module needs to be installed
3. The user-defined module needs to be imported through absolute path or relative path
2.3.2 module classification
In the process of introducing the node module, there are generally three steps

  1. path analysis
  2. File location
  3. Compilation execution

The core module will omit the two steps of file location and compilation execution, and will give priority to judgment in path analysis, and the loading speed is faster than that of general modules.
File module – a module introduced externally, such as node_ The modules installed through NPM in modules, or a JS file or JSON file written by ourselves in our project. The introduction process of file module needs to go through the above three steps.
2.3.3 path analysis
Both the core module and the file module need to go through the step of path analysis.
Node supports the following forms of module identifiers

//Core module
require('http')
----------------------------
//File module
//With Relative path at the beginning of (can be without extension)
require('./a.js')
//With Relative path at the beginning of (can be without extension)
require('../b.js')
//Absolute path starting with / without extension
require('/c.js')
//External module name
require('express')
//A file in an external module
require('codemirror/addon/merge/merge.js');

For the introduction of strings, the node will give priority to finding the matching core module in memory. If the matching is successful, it will not continue to find
(1) For example, when the HTTP module is required, priority will be given to successful matching from the core module
If the core module does not match successfully, it is classified as a file module
(2) In For identifiers beginning with / and, require will convert this relative path or absolute path into a real path according to the current file path, which is the most common path resolution
(3) Non path file modules, such as’ express’ and ‘codemirror / addon / merge / merge JS’, this kind of module is a special file module, which is generally called user-defined module.

Finding custom modules is the most time-consuming, because there is a module path for custom modules, and node will find them recursively according to this module path.

Module path – the module path of node is an array, and the module path is stored in module On the paths attribute. The generation rules of module path are as follows:

  1. Node under current path file_ Modules directory
  2. Node under parent directory_ Modules directory
  3. Node under parent directory of parent directory_ Modules directory
  4. Recurse up the path level by level until the node under the root directory_ Modules directory

2.3.4 document positioning
Extension analysis
When we use require, we sometimes omit the extension. How can node locate the specific file?

In this case, the nodes will follow the js、. json、. The order of nodes is matched once. (. Node is the file generated after compiling the C + + extension file)

If the extension matching fails, it will be treated as a package. I directly understand it as NPM package here

Package processing
For the current package directory, the package will be found first JSON (common JS package specification) passed JSON Parse() parses out the package description object and locates the next step according to the entry file name specified by the main attribute.

If the file is missing an extension, it will be located according to the extension analysis rules.

If the file name specified in main is wrong or there is no package at all JSON, node will take the index under the package directory as the default file name.

Then match index js、index. json、index. node。

If the above steps fail to locate successfully, it will enter the next module path – node under the parent directory_ Search in the modules directory until you find the node in the root directory_ If modules are not located, an exception will be thrown that fails to find them.
2.3.5 module compilation
. JS file — compile and execute after synchronously reading the file through FS module
. node file – an extension file written in C / C + +, which loads the file generated by the final compilation through the dlopen () method.
. JSON — after reading the file synchronously through the FS module, use JSON Parse() parses the returned result.
Other extension files. They are all treated as JS file loading.

2.4 commonjs module loading mechanism

Many places on the Internet say that the loading mechanism of commonjs module is that the input is a copy of the output value. This sentence is wrong.
Take the following code as an example:

// index.js
const { ss } = require('./lib');
const lib = require('./lib');
console.log('ss', ss);
console.log('lib', lib);
setTimeout(()=>{
    console.log('ss', ss);
    console.log('lib', lib);
},3000);
// lib.js
module.exports.ss = 'ss1';
setTimeout(()=>{
    module.exports.ss = 'ss2';
    console.log('module.exports', module.exports);
},2000);
//Execution results
ss ss1
lib { ss: 'ss1' }
lib module.exports { ss: 'ss2' }
ss ss1
lib { ss: 'ss2' }

As can be seen from the execution results

Commonjs exports module Exports this object, export values. Adding new attributes to this object will affect the imported values.
const { ss } = require(‘./lib’); Equivalent to const {SS} = {SS: ‘SS1’}; Deconstruction assignment, equivalent to const SS = ‘SS1’; Therefore, modifying SS for the exported object cannot make the imported object SS become 2.

III. esmodule

ES6 realizes the module function at the language specification level. It is loaded at compile time. It can completely replace commonjs and AMD specifications and become a general module solution for browsers and servers

3.1 use of ES6 module – export

//Export variables
export var name = 'pengpeng';
//Export a function
export function foo(x, y){}
//Recommended common export methods
// person.js
const name = 'dingman';
const age = '18';
Const addr = 'Karst Forest';

export { name, age, addr };

//As usage
const s = 1;
export {
  s as t,
  s as m, 
}

3.2 use of ES6 module – Import

//General usage
import { name, age } from './person.js';
//As usage
import { name as personName } from './person.js';

//Overall loading
import * as person from './person.js';
console.log(person.name);
console.log(person.age);

3.3 use of ES6 module – export default

In fact, export default is widely used in projects. Generally, we use the export default command for a Vue component or react component. It should be noted that when using the export default command, import does not need to add {}. Instead of using export default, {} must be added to import. An example is as follows:

//person.js
export function getName() {
 ...
}
//my_module
import {getName} from './person.js';

-----------------Contrast---------------------

//person.js
export default function getName(){
 ...
}
//my_module
import getName from './person.js';

Export default actually exports a variable called default, so it cannot be followed by a variable declaration statement.

It is worth noting that we can use both export and export default

//person.js
export name = 'dingman';
export default function getName(){
  ...
}

//my_module
import getName, { name } from './person.js';

3.4 loading difference between ES6 module and commonjs module

The design idea of ES6 module is to be static as much as possible, so that the dependencies of the module and the input and output variables can be determined at compile time. Therefore, ES6 is loaded at compile time, which is different from the run-time loading of commonjs (actually loading an entire object). ES6 module is not an object, but explicitly specifies the output code through the export command, and the input is also in the form of static command:

//ES6 module
import { basename, dirname, parse } from 'path';

//Commonjs module
let { basename, dirname, parse } = require('path');

What is the difference between the above writing method and the module loading of commonjs?

When the path module is required, commonjs will run the path module once, return an object, and cache the object, which contains all APIs of the path module. No matter how many times the module is loaded in the future, the cached value is taken, that is, the result of the first run, unless it is cleared manually.
ES6 will load only three methods from the path module, and the others will not be loaded. This is loading at compile time. ES6 can complete module loading at compile time. When ES6 encounters import, it will not execute the module like commonjs, but generate a dynamic read-only reference. When it is really needed, it will take values in the module. Therefore, ES6 module is a dynamic reference and will not cache values.

IV. summary

The above introduces some knowledge of modularity, welcome to criticize and correct!