1. Modular development

1.1 Modular Background

Modularity is a very pressing need for JavaScript:

  • But JavaScript itself didn’t come up with its own modularity solution until ES6 (2015);
  • Before that, there were many different modularity specifications for JavaScript to support modularity: AMD, CMD, CommonJS, etc.

1.2 There is no modularity problem

Ming and Li are working on a project at the same time and will put their JavaScript code in a separate JS file

// Aaa.js developed by Xiaoming

var flag = true;
if (flag) {
  console.log("Aaa has a true flag")}Copy the code
// bbb.js developed by Xiaoli

var flag = false;
if(! flag) {console.log("BBB uses flag false");
}
Copy the code

There was clearly a problem:

Everyone likes to use flag to store a Boolean value; But one person assigned true, one person assigned false; It doesn’t matter if you don’t use it anymore; However, Ming also developed the ccC. js file:

// Xiao Ming developed ccC.js

if (flag) {
  console.log("Using the AAA flag");
}
Copy the code

The problem came: Xiao Ming found that the flag value in CCC was wrong

  • For the intelligent you, of course, you can see at a glance, is xiaoli set flag to false;
  • But if each file has thousands or more of code, and there are hundreds of files, can you tell at a glance where the Flag has been changed?

Note: The reference path is as follows:

<script src="./aaa.js"></script>
<script src="./bbb.js"></script>
<script src="./ccc.js"></script>
Copy the code

So, no modularity is disastrous for a large project.

Of course, there is a solution to this problem: immediate function call Expressions (IIFE)

// aaa.js

const moduleA = (function () {
  var flag = true;

  if (flag) {
    console.log("Aaa has a true flag")}return {
    flag: flag
  }
})();
Copy the code
// bbb.js

const moduleB = (function () {
  var flag = false;

  if(! flag) {console.log("BBB uses flag false");
  }
})();
Copy the code
// ccc.js

const moduleC = (function() {
  const flag = moduleA.flag;
  if (flag) {
    console.log("Using the AAA flag");
  }
})();
Copy the code

Have you solved the naming conflict problem? Resolved. But we’ve actually created a new problem:

  • First, I must remember the name of the object returned in each module so that I can use it correctly in other modules.
  • Second, the code is messy and needs to be wrapped in an anonymous function for each file.
  • Third, in the absence of proper specifications, everyone and every company can have arbitrary names, even the same module names;

As a result, we find that while modularity is implemented, our implementation is too simple and non-canonical.

  • We need to have a discipline that binds everyone to write modular code;
  • Core functionality should be included in the specification: the module itself can export exposed properties, and the module can import its own required properties;

The JavaScript community has sprung up with a number of useful specifications to address these issues, and we’ll take a look at some representative ones.

2. The CommonJS specification

ES Module specification

Es import/export properties are read-only and cannot be modified when imported by other modules. But you can modify properties in an object, not an object. Exported variables cannot be modified either

3.1 the export export

// Add the export keyword directly before the statement declaration
export const name = 'coderwhy';
export const age = 18;
export let message = "my name is why";

export function sayHello(name) {
  console.log("Hello " + name);
}


// Method 2: Place all identifiers to be exported in {} next to export
{} is not an enhanced representation of an ES6 object literal. {} is not an object literal.
// - export {name: name};

const name = 'coderwhy';
const age = 18;
let message = "my name is why";

function sayHello(name) {
  console.log("Hello " + name);
}

export {
  name,
  age,
  message,
  sayHello
}


// Method 3: Give an alias to the identifier when exporting
export {
  name as fName,
  age as fAge,
  message as fMessage,
  sayHello as fSayHello
}
Copy the code

3.2 the default usage

Note: There can only be one default export in a module;

Exports: named exports

  • The name is specified when exporting export;
  • You need to know the specific name when importing an import;

Another export is called a default export.

  • By default, export can be exported without specifying a name.
  • You don’t need to use {} when importing, and you can specify the name yourself.
export default function sub(num1, num2) {
  return num1 - num2;
}
Copy the code
import sub from './modules/foo.js';

console.log(sub(20.30));
Copy the code

3.3 Import Keywords

The import keyword is responsible for importing content from another module. There are several ways to import content:

import{identifier list}from 'modules';Copy the code

Note that {} is not an object either. It just holds the contents of the imported identifier list.

// Method 1:
import { name, age, message, sayHello } from './modules/foo.js';


// Method 2: Alias the identifier during import
import { name as wName, age as wAge, message as wMessage, sayHello as wSayHello } from './modules/foo.js';

// Put module functions on a Module object

import * as foo from './modules/foo.js';

console.log(foo.name);
console.log(foo.message);
console.log(foo.age);
foo.sayHello("Kobe");
Copy the code

3.4 the import ()

Loading a module with import is not possible in logical code, such as:

if (true) {
  import sub from './modules/foo.js';
}
Copy the code

Why does this happen?

  • This is because the ES Module must know its dependencies when it is parsed by the JS engine;
  • Since the JS code is not running at this time, it is not possible to make a judgment like if based on the code execution;
  • Even the following is wrong: we must be able to determine the value of path at runtime;
const path = './modules/foo.js';

import sub from path;
Copy the code

But in some cases, we do want to load a module dynamically:

If you do not understand the conditions, dynamically select the path to load the module; At this point we need to use the import() function to load dynamically;

// aaa. Js module:
export function aaa() {
  console.log("Aaa is printed");
}
export let mm = 2


/ / BBB. Js module:
export function bbb() {
  console.log("BBB is executed.");
}

/ / main. Js module:
let flag = true;
if (flag) {
  import('./modules/aaa.js').then(a= >{ a.aaa(); })}else {
  import('./modules/bbb.js').then(b= >{ b.bbb(); })}Copy the code

Import (‘./modules/aaa.js’) returns a promise object after loading aaa.js. Then a is the exported object from aaa.js

a: {
aaa: function(){... }mm: 2
}

b: {
bbb: function(){...}
}
Copy the code

3.5 Node supports ESModule

You need to change the.js suffix to.mjs

4. AMDCMD specification

reference

Juejin. Cn/post / 689452…