首页 > 解决方案 > 使用 webpack 为 react-lottie 从主包中排除 JSON 文件

问题描述

在我们的 Web 应用程序中,我们有一些 JSON 文件,每个文件大约 10-80k 行。这些都包含在我们的主包中。这些由名为 react-lottie 的动画插件使用。

我们的一个例子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
  }
};

预期的行为是什么?

应该有一种方法可以从主包中排除 .json 文件。我试过 File-Loader、json-loader 和 const someJson = require(./someJson)

其他相关信息:webpack 版本:4.16.1 Node.js 版本:10.12.0

操作系统:Mac OS 10.14 Mojave

在下面回答(至少我是如何解决的)。如果没有任何数据,我无法初始化 lottie。

标签: jsonreactjswebpackreact-router

解决方案


预期的行为是 JSON 将被捆绑,因为它可能在运行时同步需要。JSON 数据不同于图像文件之类的东西,这些文件由浏览器异步加载,因为它们通过src属性等呈现在页面上。

如评论所述,您应该使用代码拆分。如果您安装和使用插件,最新版本的 Webpack 支持动态导入。@babel/plugin-syntax-dynamic-import

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

然后在babel.config.js

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

例子

假设您有一个 React 组件,它可能需要一些 JSON 数据,但不需要将其作为包的一部分同步加载。您的代码拆分版本可能如下所示:

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>
  }
}

相反,您可以使用动态导入 - 基本上是运行时导入,它返回一个 Promise,您可以使用它来异步加载一些与您的包分开分块的数据:

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>
  }
}

或者,您可以使用 React 的新API(v16.6.0 及更高版本)lazy动态导入与捆绑包分开分块的 React 组件。如果您想将组件及其相应的 JSON 数据分块在一起,但与主包分开,这可能会更可取:Suspense

// 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>;
  }
}

在上面的示例中,<MyComponent />其相应的代码(包括 JSON 数据)只会在组件在运行时实际呈现时加载。


推荐阅读