Deep understanding of ES6 Modules

Learn more about ES6 Modules

At present, almost all of our projects are developed based on Web pack, roll up and other construction tools, and modularity is the norm.

We are not new to it. Today, we will review the module mechanism of ES6 systematically, and summarize the commonly used operations and best practices, hoping to help you.

Some simple background

It's a mechanism we all want to implement.

The same is true in Javascript. A large Javascript program is divided into different parts. If you want to use any part, you can take that part.

For a long time, NodeJS has such capabilities. Later, more and more libraries and frameworks have modular capabilities, such as CommonJS, AMD based implementation (such as RequireJs), and subsequent Webpack, Babel, etc.

By 2015, a standard modular system was born, which is the leading role of ES6 model system.

At a glance, we don't find that the model system of ES6 is very similar to the CommonJS syntax. After all, the model system of ES6 has come from the era of CommonJS and is deeply influenced by CommonJS.

Take a simple example, for example, in CommonJs:( https://flaviocopes.com/commo...

//file.js
module.exports = value;

// Introducing value
const value = require('file.js')

In ES6:

// const.js
export const value = 'xxx';


import { value } from 'const.js'

The grammar is very similar.

Let's take a look at import and export and several related features to learn more about ES6 Modules.

Benefits of modularity

The benefits of modularity are mainly two points:

1. Avoid global variable pollution
 2. Handle dependency effectively

With the evolution of the times, browser native also began to support es6 import and export syntax.

Let's start with a simple example:

<script type="module">
  import { addTextToBody } from '/util.js';

  addTextToBody('Modules are pretty cool.');
</script>

// util.js 
export function addTextToBody(text) {
  const div = document.createElement('div');
  div.textContent = text;
  document.body.appendChild(div);
}

If you want to handle events, the same is true. Here is a simple example:


<button id="test">Show Message</button>
<script type="module" crossorigin src="/showImport.js"></script>

// showImport.js
import { showMessage } from '/show.js'

document.getElementById('test').onclick = function() {
  showMessage();
}

// show.js
export function showMessage() {
  alert("Hello World!")
}

If you want to run this demo, pay attention to a simple service:

$ http-server

Otherwise, you will see a CORS throw wrong.

As for the specific reasons and other details of the mistake, it is not the focus of this article, and you can read the following links for details.

https://jakearchibald.com/201...

Strict mode

https://developer.mozilla.org...

'use strict' declaration is not new to us. It is often used in the era of es5. It is usually added at the top of the file to disable the unfriendly part of Javascript, which helps us write more rigorous code.

This feature is enabled by default in es6 syntax. If there is less strict code in the code, an error will be reported, for example:

Here are some of the parts I extracted from MDN that are disabled in strict mode:

  • Variables can't be left undeclared
  • Function parameters must have unique names (or are considered syntax errors)
  • with is forbidden
  • Errors are thrown on assignment to read-only properties
  • Octal numbers like 00840 are syntax errors
  • Attempts to delete undeletable properties throw an error
  • delete prop is a syntax error, instead of assuming delete global[prop]
  • eval doesn't introduce new variables into its surrounding scope
  • eval and arguments can't be bound or assigned to
  • arguments doesn't magically track changes to method parameters
  • arguments.callee throws a TypeError, no longer supported
  • arguments.caller throws a TypeError, no longer supported
  • Context passed as this in method invocations is not "boxed" (forced) into becoming an Object
  • No longer able to use fn.caller and fn.arguments to access the JavaScript stack
  • Reserved words (e.g protected, static, interface, etc) cannot be bound

Several uses of exports

ES6 module only supports static export. You can only use export in the outermost scope of the module, not in conditional statements or function scopes.

From the perspective of classification, there are three main types of exports:

  1. Named Exports (Zero or more exports per module)
  2. Default Exports (One per module)
  3. Hybrid Exports

exports overview:

// Exporting individual features
export let name1, name2, ..., nameN; // also var, const
export let name1 = ..., name2 = ..., ..., nameN; // also var, const
export function functionName(){...}
export class ClassName {...}

// Export list
export { name1, name2, ..., nameN };

// Renaming exports
export { variable1 as name1, variable2 as name2, ..., nameN };

// Exporting destructured assignments with renaming
export const { name1, name2: bar } = o;

// Default exports
export default expression;
export default function (...) { ... } // also class, function*
export default function name1(...) { ... } // also class, function*
export { name1 as default, ... };

// Aggregating modules
export * from ...; // does not set the default export
export * as name1 from ...;
export { name1, name2, ..., nameN } from ...;
export { import1 as name1, import2 as name2, ..., nameN } from ...;
export { default } from ...;

Let me introduce the common usage of exports.

1. Named exports (export each function / variable)

Named export, this way to export multiple functions, generally using scenarios such as utils, tools, common and other tool class function sets, or all site unified variables.

Just add the export keyword before the variable or function.

//------ lib.js ------
export const sqrt = Math.sqrt;

export function square(x) {
    return x * x;
}
export function diag(x, y) {
    return sqrt(square(x) + square(y));
}

//------main.js usage 1------
import { square, diag } from 'lib';
console.log(square(11)); // 121
console.log(diag(4, 3)); // 5

//------main.js usage 2------
import * as lib from 'lib';
console.log(lib.square(11)); // 121
console.log(lib.diag(4, 3)); // 5

We can also directly export a list. For example, the above lib.js can be rewritten as:

//------ lib.js ------
const sqrt = Math.sqrt;
function square(x) {
    return x * x;
}
function add (x, y) {
    return x + y;
}
export { sqrt, square, add }

2. Default exports (export a default function / class)

This method is relatively simple, and is generally used for a class file or a function file with a single function.

There can only be one export default output in a module.

There are two main differences between export default and export:

You do not need to know the specific variable name of the export. You do not need {} when importing

//------ myFunc.js ------
export default function () {};

//------ main.js ------
import myFunc from 'myFunc';
myFunc();

Export a class

//------ MyClass.js ------
class MyClass{}

export default MyClass;

//------ Main.js ------
import MyClass from 'MyClass';

Note that {} is not required for the default export.

3. Mixed exports

Mixed export, that is, the combination of the first and second points above. For example, Lodash is a common combination.

//------ lib.js ------
export var myVar = ...;
export let myVar = ...;
export const MY_CONST = ...;

export function myFunc() {
  // ...
}
export function* myGeneratorFunc() {
  // ...
}
export default class MyClass {
  // ...
}

// ------ main.js ------
import MyClass, { myFunc } from 'lib';

Another example is lodash:

//------ lodash.js ------
export default function (obj) {
  // ...
};
export function each(obj, iterator, context) {
  // ...
}
export { each as forEach };

//------ main.js ------
import _, { forEach } from 'lodash';

4. Re exporting

In general, the export output variable is the name defined in the original file, but you can also use the as keyword to specify the alias, which is generally used to simplify or semantize the function name of export.

//------ lib.js ------
export function getUserName(){
  // ...
};
export function setName(){
  // ...
};

//Output alias. When import ing, you can use both the original function name and alias
export {
  getName as get, //Allow two output with different names
  getName as getNameV2,
  setName as set
}

5. Module Redirects

Sometimes, in order to avoid importing too many modules into the upper module, we may use the lower module as a transit, and directly export the contents of another module as follows:

//------ myFunc.js ------
export default function() {...};
 
//------ lib.js ------
export * from 'myFunc';
export function each() {...};
 
//------ main.js ------
import myFunc, { each } from 'lib';

export It only supports static export at the outermost layer, only export variables, functions and classes. The following usage is wrong.

`error`Of export usage:

//Value of direct output variable
export 'Mark';

// Brackets not used or default not added
// If there is only one export number, you need to add default, or use brackets
var name = 'Mark';
export name;

//export does not output variables within the scope of a block
function () {
  var name = 'Mark';
  export  { name };
}

Several uses of import

The usage of import corresponds to that of export one by one, but import supports static import and dynamic import. Dynamic import supports later and has poor compatibility.

Let me summarize the basic usage of import:

1. Import All things

When there are multiple functions or variables in export, such as the first point in the article, you can use the * as keyword to export all functions and variables, and the name followed by as is the namespace of the module.

//Export all functions and variables of lib
import * as lib from 'lib';

//Call with lib as namespace, similar to object
console.log(lib.square(11)); // 121

2. Import a single/multiple export from a module

Import single or multiple functions from the module file. Different from * as namepage, this is an on-demand import. Here is an example:

//Import square and diag functions
import { square, diag } from 'lib';

// Import only one function of square
import { square } from 'lib';

// Import default module
import _ from 'lodash';

// Import the default module and single function, which is mainly to simplify the call of single function
import _, { each } from 'lodash';

3. Rename multiple exports during import

Like export, you can also use as keyword to set alias. When the names of two classes of import are the same, you can use as to reset the names of import modules, or you can use as to simplify the names.
For example:

// Simplify function names with as
import {
  reallyReallyLongModuleExportName as shortName,
  anotherLongModuleName as short
} from '/modules/my-module.js';

// Avoid duplication
import { lib as UserLib} from "alib";
import { lib as GlobalLib } from "blib";

4. Import a module for its side effects only

Sometimes we just want to import a module, such as a style, or a class library.

// Import style
import './index.less';

// Import class library
import 'lodash';

5. Dynamic Imports

Static import will download all module resources at the first load

In our actual development, sometimes we need dynamic import.

For example, click a tab to load some new modules:


// When dynamic import, a promise is returned
import('lodash')
  .then((lodash) => {
    // Do something with lodash.
  });

// The above sentence is actually equivalent to
const lodash = await import('lodash');

New usage of es7:

async function run() {
    const myModule = await import('./myModule.js');

    const { export1, export2 } = await import('./myModule.js');

    const [module1, module2, module3] =
        await Promise.all([
            import('./module1.js'),
            import('./module2.js'),
            import('./module3.js'),
        ]);
}

run();

summary

Above, I summarized the simple background and common import and export usage of ES6 Module, but this is far from all of it, and the space is limited. If you want to know more, you can see the following extended reading part (the quality is good, you can see it).

Last

If you think the content is helpful, you can pay attention to my public number "front-end e advanced", grasp the latest information, learn together and grow together!

Extended reading:

ECMAScript 6 modules: the final syntax

JavaScript modules

dynamic-import

If you don't understand the require and import in Node, you will be hit hard

https://www.zhangxinxu.com/wordpress/2018/08/browser-native-es6-export-import-module/

Tags: node.js Javascript less Webpack ECMAScript

Posted on Fri, 08 Nov 2019 00:17:15 -0800 by shneoh