Code Splitting - Using import()
Dynamic import
currently, a "function-like"import() module loading syntax proposal is on the way into ECMAScript.
The ES2015 Loader spec defines import() as method to load ES2015 modules dynamically on runtime.
webpack treats import() as a split-point and puts the requested module in a separate chunk. import() takes the module name as argument and returns a Promise:import(name) -> Promise
index.js
function determineDate() {
import('moment').then(function(moment) {
console.log(moment().format());
}).catch(function(err) {
console.log('Failed to load moment', err);
});
}
determineDate();
Keep in mind that import() path cannot be fully dynamic (e.g., import(Math.random())). Rather either completely static (e.g., import('./locale/de.json')) or partially static (e.g., import('./locale/' + language + '.json')).
Promise polyfill
import() relies on Promise internally.
If you useimport() with older browsers, remember to shim Promise
using a polyfill such as es6-promise
or promise-polyfill
.
In an entry point of your application:
import Es6Promise from 'es6-promise';
Es6Promise.polyfill();
// or
import 'es6-promise/auto';
// or
import Promise from 'promise-polyfill';
if (!window.Promise) {
window.Promise = Promise;
}
// or ...
Usage with Babel
If you want to use import
with Babel, you'll need to install/add the syntax-dynamic-import
plugin while it's still Stage 3 to get around the parser error. When the proposal is added to the spec this won't be necessary anymore.
npm install --save-dev babel-core babel-loader babel-plugin-syntax-dynamic-import babel-preset-es2015
# for this example
npm install --save moment
index-es2015.js
function determineDate() {
import('moment')
.then(moment => moment().format('LLLL'))
.then(str => console.log(str))
.catch(err => console.log('Failed to load moment', err));
}
determineDate();
webpack.config.js
module.exports = {
entry: './index-es2015.js',
output: {
filename: 'dist.js',
},
module: {
rules: [{
test: /\.js$/,
exclude: /(node_modules)/,
use: [{
loader: 'babel-loader',
options: {
presets: [['es2015', {modules: false}]],
plugins: ['syntax-dynamic-import']
}
}]
}]
}
};
Not using the syntax-dynamic-import plugin will fail the build with
-
Module build failed: SyntaxError: 'import' and 'export' may only appear at the top
or Module build failed: SyntaxError: Unexpected token, expected {
Usage with Babel and async/await
To use ES2017 async /await
with import()
:
npm install --save-dev babel-plugin-transform-async-to-generator babel-plugin-transform-regenerator babel-plugin-transform-runtime
index-es2017.js
async function determineDate() {
const moment = await import('moment');
return moment().format('LLLL');
}
determineDate().then(str => console.log(str));
webpack.config.js
module.exports = {
entry: './index-es2017.js',
output: {
filename: 'dist.js',
},
module: {
rules: [{
test: /\.js$/,
exclude: /(node_modules)/,
use: [{
loader: 'babel-loader',
options: {
presets: [['es2015', {modules: false}]],
plugins: [
'syntax-dynamic-import',
'transform-async-to-generator',
'transform-regenerator',
'transform-runtime'
]
}
}]
}]
}
};
import supersedes require.ensure?
Good news: Failure to load a chunk can be handled now because they are Promise based.
Caveat: require.ensure allows for easy chunk naming with the optional third argument, but import API doesn't offer that capability yet. If you want to keep that functionality, you can continue using require.ensure.
require.ensure([], function(require) {
var foo = require("./module");
}, "custom-chunk-name");
System.import is deprecated
The use of System.import
in webpack did not fit the proposed spec, so it was deprecated in v2.1.0-beta.28 in favor of import()
.