First, summarize these modules:

  1. ES module. The ES module is part of the ECMAScript2015 (ES6) language specification. Before modules, multiple JavaScript files were introduced, and the top-level variables defined in the JavaScript files were global, The developer must use IIFE (Immediately Invoked Function Expression) or define namespaces to avoid problems with shared global variables. And after the module appears, each (includingexport/importJavaScript files are a module, and variables defined in a module are scoped within the file.
  2. CommonJS. While JavaScript can only be used to write web applications, CommonJS provides apis that allow JavaScript to write programs for different JavaScript interpreters and host runtime environments. CommonJS is typically used on the server (Node.js). Browsers do not support CommonJS and require translation. Module in the CommonJS specificationrequireIntroduction,exportsThe export.
  3. AMD. The API provided by AMD (Asynchronous Module Definition) specifies a mechanism for defining modules so that modules and their dependencies can be loaded asynchronously. AMD usedefineDefines modules, primarily for the browser environment. Require.js uses AMD.
  4. UMD. UMD (Universal Module Definition) is a pattern that provides compatibility with today’s most popular script loaders. (Although it is today, the latest update of UMD code from the repository is 3 years ago. At that time, UMD mainly supported AMD and CommonJS. You can use the idea of UMD, such as considering the compatibility of ES6 modules.)
  5. SystemJS. SystemJS is a configurable module loader. useSystem.registerMethod to register the module, usingSystem.importMethod to import a module.

In my actual development, I use ES native module most, CommonJS is used less, AMD, UMD, SystemJS is basically not used. So this article will focus on the ES and CommonJS modules, with less on the other modules. Overall, it is a simple description of each module, without in-depth study.

Source address.

ES module

The two tables referenced in the ECMAScript Module documentation provide an overview of how ES modules are imported and exported.

Import

Table 1: Mapping between import forms and ImportEntry Records

Import Statement Form [[ModuleRequest]] [[ImportName]] [[LocalName]]
import v from "mod"; “mod” “default” “v”
import * as ns from "mod"; “mod” “*” “ns”
import {x} from "mod"; “mod” “x” “x”
import {x as v} from "mod"; “mod” “x” “v”
import "mod";

ImportEntry Record is a proper term. ImportEntry Record contains [[ModuleRequest]], [[ImportName]], and [[LocalName]].

[[ModuleRequest]] refers to the module declarator in the import declaration. For example, in import ‘./a.js’, the string ‘./a.js’ is the module declarator.

[[ImportName]] refers to the name to be bound to the module marked by [[ModuleRequest]]. Import {x as v} from “b”; import {x as v} from “b”; Then [[ImportName]] refers to the name of x in B, i.e. “x”. * indicates that the import requests the namespace object of the target module. A Module Namespace object is a Module Namespace alien that provides run-time attribute-based access to a Module’s exported binding. Simply put, namespace objects are used to access the entire content of the module’s export. When using import * as ns from “mod”; When content is imported this way, such an object (namespace object) is created for the imported module.

[[LocalName]] refers to the name of the value of the imported module that is accessed locally within the imported module. Import {x as v} from “b”; import {x as v} from “b”; , then in module A, use the name v to access x imported from module B.

Using the import “mod”; No ImportEntry Record was created when

Export export

Table 2: Mapping between exported form and ExportEntry Records

Export Statement Form [[ExportName]] [[ModuleRequest]] [[ImportName]] [[LocalName]]
export var v; “v” null null “v”
export default function f() {} “default” null null “f”
export default function () {} “default” null null default
export default 42; “default” null null default
export {x}; “x” null null “x”
export {v as x}; “x” null null “v”
export {x} from "mod"; “x” “mod” “x” null
export {v as x} from "mod"; “x” “mod” “v” null
export * from "mod"; null “mod” “*” null
export * as ns from "mod"; “ns” “mod” “*” null

ExportEntry Record is a given term, which includes [[ExportName]], [[ModuleRequest]], [[ImportName]], [[LocalName]].

[[ExportName]] refers to the name used to export the binding for this module.

[[ModuleRequest]], [[ImportName]], [[LocalName]] are similar to the ExportEntry Record.

A circular reference

Create an exercise project create-app es-module. Create a util folder under the SRC directory of the es-module file. Create three files in the util folder, a.js, B.JS, and C.JS. A references B and C, and B references A.

A. js File contents:

import b from './b.js';
import { printC } from './c.js';

printC();

export default {
  a: 'aaa'.ab: b,
};
Copy the code

B. Js File contents:

import a from './a.js';

export default {
  b: 'bbb'.ba: a,
};
Copy the code

C. js File contents:

export const printC = function printC () {
  console.log('ccc');
};
Copy the code

Introduce a in app.tsx:

import a from '@/util/a';

console.log(a);
Copy the code

performnpm startStart the project and observe the following print out in the browser:First, we execute a.js, a refers to B, and when we get to b, we refer to a, so we go back to A, and a hasn’t finished yet, so B gets undefined, and b finishes, A gets B, A gets C, and we execute, export the object.

import()

Import and export can only be used at the top level of the file, while import() can be used anywhere in the file, loading the module asynchronously.

Add a new file d.js with the following contents:

function printD () {
  console.log('ddd');
}

export default printD;
Copy the code

Introduced in B. JS:

import a from './a.js';

if(! a) {import('./d.js').then((moduleD) = > {
    moduleD.default();
  });
}
export default {
  b: 'bbb'.ba: a,
};
Copy the code

The printout reads as follows:

Modify the properties of the imported object

Modify the contents of A.js as follows:

import b from './b.js';
import { printC } from './c.js';

b.ba = 'test';

if (b.ba) {
  import('./b.js').then((moduleB) = > {
    console.log(moduleB.default, 'moduleB.default');
  });
}

printC();

export default {
  a: 'aaa'.ab: b,
};
Copy the code

Add content to B.js:

.console.log('B executed');

export default {
  b: 'bbb'.ba: a,
};
Copy the code

The contents printed by the console are as follows:Only one was printedB to perform theB. js is cached after it is executed only once. All objects imported after b are exported after b is executed are imported. In a.js, we changed the BA attribute of the exported object of module B to test, and then used import to load module B asynchronously. The BA attribute of the content of module B loaded asynchronously has changed to test. My guess is that b is cached after execution, exposing a reference to b’s exported object (suppose the object is b). When module B is introduced into module A, it gets a reference to B, so it is executed in module Ab.ba = 'test';B will be changed directly. When you import B again, you will get the changed content.

Note: This is just an exercise in writing circular references and changing the properties of imported objects to see what happens. It is best not to use this method in actual development so as not to dig yourself a hole.

CommonJS

With CommonJS, you can use JavaScript to write server-side JavaScript applications, command-line tools, desktop GUI-based applications, and hybrid applications.

Use Node.js to practice CommonJS. In Node.js, each file is treated as a module. Node.js already supports ECMAScript modules, but it’s experimental. You can check out Node.js ECMAScript modules.

Import (require)

Require () returns the API exported by the external module. Require handles relative paths and non-relative module names differently.

Require (relative path)

For example, if you use var x = require(‘./moduleB’) in /root/src.modulea.js, Node.js will process the files in the following order:

  1. Look for the moduleB.js file in the sibling directory. (i.e./root/src/moduleB.js)
  2. Check whether the moduleB folder (/root/src/moduleB), whether there is a package.json file in the folder, and whether there is a main property in package.json, if the value of the main property is as follows:{ "main": "lib/mainModule.js" }Then Node.js will point to/root/src/moduleB/lib/mainModule.js.
  3. View folders/root/src/moduleBIs there an index.js file, which is implicitly considered to be the “main” module of the folder?

Require (non-relative module name)

Var x = require(“moduleB”); var x = require(“moduleB”); If a module name is introduced in a non-relative path, the module will be processed in the following order: first, node_modules in the current directory will be searched for moduleB, then node_modules in the upper directory will be searched, and finally the global node_modules will be searched.

  1. /root/src/node_modules/moduleB.js

  2. / root/SRC/node_modules/moduleB/package. The json (if the statement “main” attribute)

  3. /root/src/node_modules/moduleB/index.js

  4. /root/node_modules/moduleB.js

  5. / root/node_modules/moduleB/package. Json (if the statement “main” attribute)

  6. /root/node_modules/moduleB/index.js

  7. /node_modules/moduleB.js

  8. / node_modules/moduleB/package. Json (if the statement “main” attribute)

  9. /node_modules/moduleB/index.js

Exports

Exports objects from modules using module.exports. Exports is short for module.exports, and module.expots was assigned to exports before module evaluation. (exports = module.exports)

Export content in the following three ways:

module.exports = {
  b1: '111'.b2: '222'.b3: '333'};Copy the code
module.exports.b1 = '111';
module.exports.b2 = '222';
module.exports.b3 = '333';
Copy the code
exports.b1 = '111';
exports.b2 = '222';
exports.b3 = '333';
Copy the code

The effect of these three methods is the same, and the output is:

{
	b1: '111'.b2: '222'.b3: '333',}Copy the code

But writing like this does not export the same object as above, it just assigns the object to the exports variable.

exports = {
	b1: '111'.b2: '222'.b3: '333'};Copy the code

Create a new folder Commons, use NPM init -y to initialize the project in the folder, create a file a.js, and introduce the b.js file in a.js. Insert the above sections of code into B.js, and introduce B.JS into A.JS:

const b = require('./b.js');
console.log(b);
Copy the code

Run node a.js to view the effect.

A circular reference

Change the b.js file from the previous exercise to look like this:

const a = require('./a.js');
console.log(a, 'A' from 'B');

module.exports = {
  b1: '111'.b2: '222'.b3: '333'.ba: a,
};
Copy the code

The contents of the a.js file are as follows:

const b = require('./b.js');
console.log(b, 'B' from 'A'.);
Copy the code

Run node a.js to print

{} a {b1: '111'.b2: '222'.b3: '333'.ba: {}Copy the code

A refers to B, so B starts executing. B refers to A. At this point, A has nothing to export, so B gets an empty object called A.

Modify the contents of the a.js file as follows:

exports.a1 = '111';

const b = require('./b.js');
console.log(b, 'B' from 'A'.);

exports.a2 = '222';
Copy the code

You can see that a1 is exported before b is imported. Run node a.js to print:

{ a1: '111'} a {b}b1: '111'.b2: '222'.b3: '333'.ba: { a1: '111'}} BCopy the code

When a is imported into b, it will get the content that has been exported from A at that time. If it is not exported, it will get an empty object.

Modify the properties of the imported object

Modify the contents of the a.js file as follows:

exports.a1 = '111';

let b = require('./b.js');
console.log(b, 'B' from 'A'.);

exports.a2 = '222';

b.ab = 'test';
b = require('./b.js');
console.log(b, 'B obtained after modification in A');
Copy the code

Add a console.log statement to b.js to observe how many times B. JS executes:

.console.log('B executed');

export default {
  b: 'bbb'.ba: a,
};
Copy the code

Run the node a.js command. The following output is displayed:

{ a1: '111'{b} b {b}b1: '111'.b2: '222'.b3: '333'.ba: { a1: '111'}} b {b1: '111'.b2: '222'.b3: '333'.ba: { a1: '111'.a2: '222' },
  ab: 'test'} a = b = bCopy the code

Modifying the properties of the imported module also changes the value of the module. The CommonJS module is cached once it is executed, and the reference to the API exposed by module B is obtained when the require is executed, so if a changes b in a, the content exported by module B will also be changed.

Note: This is just an exercise in writing circular references and changing the properties of imported objects to see what happens. It is best not to use this method in actual development so as not to dig yourself a hole.

AMD

The AMD specification defines a function:

define(id? , dependencies? , factory);Copy the code

Id specifies the id of the module;

Dependencies: dependencies: dependencies: dependencies: dependencies: dependencies: dependencies: dependencies: dependencies: dependencies: dependencies

Factory is a function that instantiates a module or object. The order of the dependencies array corresponds to the order of the Factory parameter.

Requirejs uses the AMD specification, and pulls in an example for two simple exercises:

git clone https://github.com/volojs/create-template.git
Copy the code

Create the following files in the app folder where you downloaded the good examples:

a.js

define('a'.function(require.exports) {
  exports.a = 'aaa';
});
Copy the code

This defines the module name and exports the content.

b.js

define(['a'].function(a) {
  return {
    b: 'bbb'.ba: a,
  }
});
Copy the code

Use a in module B as part of the export of B.

c.js

define({
  c: 'ccc'});Copy the code

When factory is an object, that object is the content of the export.

d.js

define(function(require.exports.module){
  const a = require('./a');
  const b = require('./b');
  const c = require('./c');

  module.exports = {
    d: 'ddd'.da: a,
    db: b,
    dc: c,
  };
});
Copy the code

Import d.js in main.js:

const d = require('./d');
console.log(d, 'd');
Copy the code

Open index. HTML in a browser, and the console prints the following:

Da here is undefined to indicate that modules defined in a.js must be added to the dependencies array in the same way as B.js.

UMD

The UMD pattern generally aims to provide compatibility with today’s most popular Script loaders. Use AMD as the base in many cases and add special case handling to handle CommonJS compatibility.

The UMD repository nodeAdapter code is used as an example:

(function(define) {

    define(function (require.exports.module) {
        var b = require('b');

        return function () {}; }); } (// Help Node out by setting up define.
    typeof module= = ='object' && module.exports && typeofdefine ! = ='function' ?
    function (factory) { module.exports = factory(require.exports.module); } :
    define
));
Copy the code

This code is compatible with AMD modules and CommonJS modules. If it is a CommonJS module system: Module is an object module.exports and define is not a function, redefine the define function, otherwise use the global define variable.

SystemJS

SystemJS is specifically designed to improve the performance of modules in production environments.

SystemJS also provides some examples that we can download directly:

git clone https://github.com/systemjs/systemjs-examples.git
Copy the code

In systemjs-examples/loading-code/amd-modules, you can see the way to import maps with

<script type="systemjs-importmap">
  {
    "imports": {
      "saturn": "./lib/saturn.js"}}</script>
Copy the code

This is the same as the package map, and when you import the module (system.import (‘ Saturn ‘), you will find the module in the./lib/ Saturn. Js path).

Use the mapping directly in js in the systemjs-features/nodejs-loader folder:

const { System, applyImportMap, setBaseUrl } = require('systemjs');

applyImportMap(System, {
  imports: {
    "antarctica": "./antarctica.js"}});Copy the code

Import modules using system.import:

System.import('antarctica').then(ns= > {
  console.log('antarctica module', ns);
});
Copy the code

Register a module with system. register:

System.register([], (_export) = > ({
  execute() {
    _export('default'."Antarctica is the southern continent"); }}));Copy the code

Refer to the address

  1. ECMAScript Modlue:www.ecma-international.org/ecma-262/11…
  2. Node. Js Modules:nodejs.org/dist/latest…
  3. AMD: github.com/amdjs/amdjs…
  4. UMD:github.com/umdjs/umd
  5. SystemJS:github.com/systemjs/sy…