N
N
Nikolay Dombrovsky2017-06-09 08:51:33
JavaScript
Nikolay Dombrovsky, 2017-06-09 08:51:33

How to implement dynamic import for code splitting in Webpack 2?

It would seem that everything is simple. We use a construction like:

import(/* webpackChunkName: "my-component-chunk" */ 'MyComponent').then((Component) => {
  this.setState({ Component });
})

and enjoy life. Yeah, the component works, but the chunk will not be created in the end - everything will be the same in the main.
require.ensure - same song
In webpack config (version 2):
new webpack.optimize.CommonsChunkPlugin({
  children: true,
  async: true,
}),
...
//остальные статичные чанки типа 'vendor'

What's wrong?

Answer the question

In order to leave comments, you need to log in

2 answer(s)
N
Nikolay Dombrovsky, 2017-09-04
@nDiviD

I found a solution, posted an answer to a similar question, and I'll write here. Everything is quite simple if you know the nuances:
1) You need to understand that what we want to download should be in a separate file - a chunk.
2) In order to create a chunk, you need to load all imports of this component "lazy". This is very important, at one time it was this that prevented me from splitting an existing project into chunks.
3) Well, the simplest: we load through the import promise:

const loadEditor = () => import(/* webpackChunkName: "my-best-editor-chunk" */ 'react-dart-editor');
  loadEditor().then(m => use it);

4) webpack config:
new webpack.optimize.CommonsChunkPlugin({
  children: true,
  async: true,
  minChunks: 2,
}),
new webpack.optimize.CommonsChunkPlugin('manifest'),

You can also import functions.
I am using a component like this:
Chunk.jsx
import React, { Component } from 'react';
import PropTypes from 'prop-types';

class Chunk extends Component {
  state = { LoadedComponent: null };

  componentWillMount() {
    if (this.props.preload) this.load(this.props);
  }

  componentWillReceiveProps(nextProps) {
    const { load, component, show } = nextProps;
    if (
      this.props.load !== load ||
      this.props.component !== component ||
      (this.props.show !== show && show)
    ) {
      this.load(nextProps);
    }
  }

  load(props) {
    this.setState({ LoadedComponent: null });
    props.load().then((mod) => {
      this.setState({
        LoadedComponent: props.component ? mod[props.component] : mod.default,
      });
    });
  }

  renderLoading = () => this.props.showLoading ? (<div>Loading</div>) : null;

  render() {
    const { LoadedComponent } = this.state;
    const  { show, ...props } = this.props;
    delete props.load;
    delete props.component;
    if (!show) return null;
    return LoadedComponent ? <LoadedComponent {...props} /> : this.renderLoading();
  }
}

Chunk.defaultProps = {
  showLoading: false,
  preload    : true,
  show       : true,
};

Chunk.propTypes = {
  load       : PropTypes.func.isRequired,
  show       : PropTypes.bool,
  preload    : PropTypes.bool,
  component  : PropTypes.string,
  showLoading: PropTypes.bool,
};

export default Chunk;

Use like this:
<Chunk
  load={loadEditor} // была уже выше
  component="EditorAside" // если нужно что-то, что импортируется не по дефоулту
  show={true/false} // отображать ли компотнент
  preload={true/false} // осуществлять ли предзагрузку чанка
  props1="1"
  props2="2"
/>

N
Nikita Gushchin, 2017-06-09
@iNikNik

UPD2: Solving the problem
You have the name entryPoint and that is what you need to specify as name when configuring commonChunksPlugin

entry: {
    application:  /* ... */
  },
....
plugins: {
  new webpack.optimize.CommonsChunkPlugin({
      name: 'application', // <--- Так же как и entryPoint
      async: "vendors", // <--- будет использовано как имя файла (можно оставить просто true). "vendors.js"
      children: true,
      minChunks: 2 // <--- должно быть >= 2
    })
}

It looks like this is an issue. The discussion goes here - https://github.com/webpack/webpack/issues/5109#iss...
old answer
In theory, you should have something like this:
{
  entry: {
    main: './reactStartup.js'
  },
  output: {
    filename: '[name]-bundle.js',
    chunkFilename: '[name]-chunk.js',
    path: path.resolve(__dirname, 'dist'),
    publicPath: 'dist/'
  },
  plugins: [
    new webpack.optimize.CommonsChunkPlugin({
      async: true,
      children: true,
      minChunks: 2,
    }),
  ]
}

Обратите внимание на minChunks по-умолчанию там скорей всего Inf (документация), а это значит, что в этот чанк ничего не попадает.
Но CommonsChunkPlugin просто выделяет общие модули (которые используются >= 2х раз), а для динамических импортов вам нужен отдельно плагин для babelrc:
// .babelrc
{
  "plugins": [
    "syntax-dynamic-import"
  ]
}

Еще дело может быть в модулях - я с вебпаком2 давно отключил преобразование модулей в babel ( "modules": false):
"presets": [
    [
      "env",
      {
        "targets": {
          "node": "current",
          "browsers": "last 2 versions"
        },
        "modules": false,
        "loose": true
      }
    ],
    "react",
  ],

Didn't find what you were looking for?

Ask your question

Ask a Question

731 491 924 answers to any question