简体   繁体   中英

Exclude JSON files from the main bundle with webpack for react-lottie

In our web app we have a few JSON files that are ~10-80k lines each. These are getting included in our main bundle. These are used by an animation plugin called react-lottie.

An example of our webpack.config.js

module.exports = {
  entry: ["./src/index.js"],
  module: {
    rules: [
      { test: /\.(js|jsx)$/, exclude: /node_modules/, use: ["babel-loader"] },
      {
        test: /\.(jpg|png|gif|ico)$/,
        use: {
          loader: "file-loader",
          options: { name: "[path][name].[hash].[ext]" }
        }
      }
    ]
  },
  resolve: { extensions: ["*", ".js", ".jsx"] },
  output: {
    path: __dirname + "/dist",
    publicPath: "/",
    filename: "[name].[hash].js"
  },
  plugins: [
    new webpack.HotModuleReplacementPlugin(),
    new HtmlWebpackPlugin({ hash: false, template: "src/index.html" }),
    new DashboardPlugin(),
    new CopyWebpackPlugin([
      {
        from: "src/components/Assets/BookingBar.js",
        to: "assets/BookingBar.js"
      }
    ]),
    new BundleAnalyzerPlugin()
  ],
  devServer: {
    contentBase: "./dist",
    hot: true,
    historyApiFallback: true,
    port: 4000
  }
};

What is the expected behavior?

There should be a way to exclude .json files from the main bundle. I've tried File-Loader, json-loader, and const someJson = require(./someJson)

Other relevant information: webpack version: 4.16.1 Node.js version: 10.12.0

Operating System: Mac OS 10.14 Mojave

ANSWER BELOW (AT LEAST FOR HOW I SOLVED IT). I couldn't initialize the lottie without any data.

The expected behavior is that the JSON will get bundled because it's, presumably, needed synchronously at runtime. JSON data differs from something like image files which are loaded asynchronously by the browser as they are rendered on the page via src attributes etc.

As the comments mentioned, you should be using code splitting. The latest version of Webpack supports dynamic imports if you install and use the @babel/plugin-syntax-dynamic-import plugin.

npm install --save-dev @babel/plugin-syntax-dynamic-import

Then in babel.config.js :

module.exports = {
  ...
  plugins: [
    "@babel/plugin-syntax-dynamic-import"
  ]
  ...
};

Example

Say you have a React component that might need some JSON data, but doesn't need to load it synchronously as part of the bundle. Your non -code splitted version might look something like this:

import React from 'react';
import myJSON from './myJSON.json';

export default class MyComponent extends React.Component {
  render() {
    return <div>{JSON.stringify(myJSON, null, 2)}</div>
  }
}

Instead you can use a dynamic import - basically a runtime import that returns a Promise you can use to asynchronously load some data chunked separately from your bundle:

import React from 'react';
import myJSON from './myJSON.json';

export default class MyComponent extends React.Component {
  state = {data: {}};
  componentDidMount() {
    import(/* webpackChunkName: 'myJSON' */ './myJSON.json')
      .then((data) => {
        this.setState({data});
      });
  }
  render() {
    return <div>{JSON.stringify(this.state.data, null, 2)}</div>
  }
}

Alternately, you can use React's new lazy and Suspense API (v16.6.0 and higher) to dynamically import React components that get chunked separately from the bundle. This might be preferable if you want to chunk a component and its corresponding JSON data together, but separately from the main bundle:

// MyComponent.jsx
import React from 'react';
import myJSON from './myJSON.json';

export default class MyComponent extends React.Component {
  render() {
    return <div>{JSON.stringify(myJSON, null, 2)}</div>
  }
}

// SomeParent.jsx
import React, {lazy, Suspense} from 'react';
const MyComponent = lazy(() => import(/* webpackChunkName: 'MyComponent' */ './MyComponent'));

export default class SomeParent extends React.Component {
  render() {
    return <div>
      <Suspense fallback={<div>Loading...<div>} >
        <MyComponent />
      </Suspense>
    </div>;
  }
}

In the above example, <MyComponent /> and its corresponding code -- including the JSON data -- will only be loaded when the component is actually rendered at runtime.

Ultimately I took the answer above below me but wasn't able to initialize the lottie without any JSON data. I ended up doing this:

import React, { PureComponent } from "react"
import Lottie from 'react-lottie'

export default class AnimationAutomatedCommunication extends PureComponent {

  constructor(props) {
    super(props)
    this.state = {
      animation: <div />
    }
  }

  async componentDidMount() {
    const animation = await import(/* webpackChunkName: "AnimationAutomatedCommunication" */ './JsonData/AnimationAutomatedCommunication.json')
     const defaultOptions = {
       loop: true,
       autoplay: true,
       animationData: animation.default
     }
    this.setState({
      animation: <div className={this.props.className}>
        <Lottie key="lottie-win-jobs" options={defaultOptions}
                isStopped={this.props.isStopped} />
      </div>
    })
  }

  render() {
    return (
      <React.Fragment>
        {this.state.animation}
      </React.Fragment>
    )
  }
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM