Overview
- A preference for single exports and support for cyclic dependencies.
- Support asynchronous loading and configurable module loading.
- Bundle structure could be statically analyzed for static checking, optimization etc.
The ES6 module standard has two parts:
- Declarative syntax for importing and exporting.
- Programmatic loader API: to configure how modules are loaded and to conditionally load modules.
Module Syntax
There are two kinds of exports: named exports (several per module) and default exports (one per module)
Named Exports
A module can export multiple things with the key word export
and distinguished by their names .
// lib.js
export const constant = 'constant'
function funcA () {}
function funcB () {}
export { funcA, funcB }
// main.js
import { constant, funcA, funcB } from 'lib.js'
// or you could import everything
import * as lib from 'lib.js'
Default Exports
ES6 allows to export a default value for one module (file), and then import it with a name.
// lib.js
export default function () {}
// main.js
import Func from 'lib.js'
Mixing named exports and default export in a module
It’s a common usage to have both named exports and default export in a module in the case that we want to export a standard interface as default export and also exports multiple specific values in the meanwhile.
// lib.js
function funcA () {}
function funcB () {}
let all = { funcA: funcA, funcB: funcB }
export default all
export { funcA, funcB }
// main.js
import all, { funcB } from 'lib.js'
Notice that the default export is actually a named export with the special name default
.
import { default as all } from 'lib.js'
// equivalent to
import all from 'lib.js'
const constant = 'constant'
export default constant
// equivalent to
export { constant as default }
Advantages
Static Module Structure
ES6 enforces a static structure, which means import
and export
must be determined at compile time. You can not import modules conditionally like if...else...
.
Synchronous And Asynchronous Loading
Since ES6 enforces static module structure, by default modules are synchronous loaded and executed. Conditional importing and asynchronous resolving (it means using import
directive here) is prohibited. ES6 provides import()
method for asynchronous loading and resolving, which return a promise for afterwards snippets when it’s loaded and resolved.
Support For Cyclic Dependencies
ES6 modules export bindings, not values, and therefore connection to variables declared inside module body keeps alive. This is the point quite different from commonJS, which return the instance (value) of the exporting module and every time you import it, it remains the values declared inside the module body though you change it in functions.
Circular dependencies
Circular dependencies is that importing each other in files, which is the different situation from cyclic dependencies. You might find it not work the way you want if you run across circular dependencies.
// moduleA.js
import { b } from 'moduleB.js'
console.log('b: ' + b)
const a = 'a'
console.log('module A executed')
export { a }
// moduleB.js
import { a } from 'moduleA.js'
console.log('a: ' + a)
const b = 'b'
console.log('module B executed')
export { b }
// entry main.js
import { b } from 'moduleB.js'
// b: undefined (Error Reference)
// module A executed
// a: a
// module B executed