It is impressive how well ES5 module systems work without explicit support from the language. The two most important (and unfortunately incompatible) standards are:
The above is but a simplified explanation of the current state of affairs. If you want more in-depth material, take a look at “Writing Modular JavaScript With AMD, CommonJS & ES Harmony” by Addy Osmani.
The goal for ECMAScript 6 modules was to create a format that both users of CommonJS and of AMD are happy with:
Being built into the language allows ES6 modules to go beyond CommonJS and AMD (details are explained later):
The ES6 module standard has two parts:
The following ECMAScript 6 module “is” a single function:
//------ myFunc.js ------
export default function () { ··· } // no semicolon!
export default function Animal() { ··· } // You can name the function if it needs to be used within this module.
//------ main1.js ------
import myFunc from 'myFunc';
myFunc();
An ECMAScript 6 module whose default export is a class looks as follows:
//------ MyClass.js ------
export default class { ··· } // no semicolon!
export default class Animal { ··· } //You can name the class if it needs to be used within this module.
//------ main2.js ------
import MyClass from 'MyClass';
let inst = new MyClass();
A module can export multiple things by prefixing their declarations with the keyword export. These exports are distinguished by their names and are called named exports.
//------ 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 ------
import { square, diag } from 'lib';
console.log(square(11)); // 121
console.log(diag(4, 3)); // 5
There are other ways to specify named exports (which are explained later), but I find this one quite convenient: simply write your code as if there were no outside world, then label everything that you want to export with a keyword.
If you want to, you can also import the whole module and refer to its named exports via property notation:
//------ main.js ------ import * as lib from 'lib'; console.log(lib.square(11)); // 121 console.log(lib.diag(4, 3)); // 5
The killer feature of dynamic imports is the ability to:
Yes, you could do this using AMD (require.js) or rolling your own script loader, but this could be a very good technique depending on your needs. You need to configure your tooling to handle this non-standard syntax, however. First, install these packages:
babel-plugin-syntax-dynamic-importimport() function. It does not do a transform because Webpack
already knows how to handle dynamic imports natively.babel-eslintNow edit your .babelrc file to include:
"plugins": ["syntax-dynamic-import"]
Next, edit the .eslintrc.json file.
"parser": "babel-eslint",
"parserOptions": {
"ecmaFeatures": {
"experimentalObjectRestSpread": true,
"jsx": true
},
"sourceType": "module",
"allowImportExportEverywhere": true
},
import() to load modules dynamicallyThe import() function is used to load modules (scripts) asynchronously in the
browser. It is not required, but when pulling a module dynamically, I like to use named exports.
For example,
import React from 'react'
import PropTypes from 'prop-types'
// This is kind of a weird syntax, but dynamic imports won’t automatically import the `default`
// member. So you either need to use it as Gold.default, or when written like this you can
// destructure it as: { Gold }
export function Gold (props) {
return (
<div>
<h1>Congratulations</h1>
<p>You just won {props.coins} coins!</p>
</div>
)
}
Gold.propTypes = {
coins: PropTypes.number.isRequired
}
Then, you import the module by doing this:
import React from 'react'
import './app.scss'
class App extends React.Component {
constructor (props) {
super(props)
this.state = {
luckyWinner: null
}
}
componentDidMount () {
const coins = parseInt(Math.random() * 100)
if (coins > 35) {
import(/* webpackChunkName: "Gold" */ './components/gold').then(({ Gold }) => {
this.setState({
luckyWinner: <Gold coins={coins} />
})
})
}
}
render () {
return (
<div>
{this.state.luckyWinner}
</div>
)
}
}
export default App