Code Splitting - Using import()

Edit this Page   

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 use import() 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 level, 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().

Examples

Weblinks


Contributors