在create-react-app中有条件地导入资源

15

在使用create-react-app创建React应用程序时,是否可以有条件地导入资产?我知道require语法-例如:

import React from "react";


const path = process.env.REACT_APP_TYPE === "app_1" ? "app_1" : "app_2";

const imagePath = require(`./assets/${path}/main.png`);

export default function Test() {
  return (
      <img src={imagePath} alt="" />
  );
}

然而,这会将我的所有资产捆绑在一起,无论是什么。

它将加载正确的图像,但最终构建仍将所有文件捆绑在一起。

当我查看最终构建的开发工具时,即使我只想加载app_1的资产,也可以看到所有资产。

如果必须更改webpack配置,那么我该怎么做?还是有其他方法?


在您的用例中,将一些不会使用的文件包含在构建中真的很糟糕吗? - Tholle
我在思考如果我有20个不同的版本,每个版本都有30个资产。从长远来看可能会有很多不必要的文件。 - jones
寻找相同的问题,但是针对纯粹的Vanilla。在一些罕见的情况下,它可能非常有用。 - Arthur
4个回答

9
在 React 不存在的时代,我们没有把资源放进 JS 文件中。我们由 CSS 决定为哪些选择器加载资源。然后,您可以为相应的元素(甚至是整个页面)切换相应的类并且它就会变化颜色、背景或者表单等等。完全神奇!
啊!那时候真是美好的时光!
上述所有内容都是真实的,我不明白为什么有人会建议以不同的方式去做。不过如果你仍想这样做(无论出于何种原因)-你可以!最新的create-react-app支持通过动态导入和代码分割懒加载任意组件。您只需要使用带括号的import()语句而不是常规的语句。与通常的语句一样,import()接受一个请求字符串并返回一个 Promise。这就是全部了。动态请求的组件的源代码不会被捆绑在一起,而是存储在单独的块中,按需加载。

之前:

import OtherComponent from './OtherComponent';

function MyComponent() {   
  return (
    <div>
      <OtherComponent />
    </div>   
  ); 
}

之后:

const OtherComponent = React.lazy(() => import('./OtherComponent'));

function MyComponent() {
  return (
    <div>
      <OtherComponent />
    </div>
  );
}

注意function MyComponent 部分是相同的。

对于那些想知道它是否与CRA或React相关,它并不是。这是一个通用的概念,可以在纯JavaScript中使用


适用于组件,但对于具有动态路径(这次取决于环境变量)的加载资产如何处理呢? - jones
我不明白动态路径有什么区别。 - jayarjo
我没有使用过这个,但是在我看来,您仍然会将资产捆绑在一起,从而永远不会使用它们,如果没有代码拆分,那么根据目前的情况,就没有任何优势。因此,除非您正在使用CRA或类似工具,否则还需要使用Webpack进行调整。 - Abulafia
动态导入是 TC39提案,所以最终它可能会直接使用,但当前情况是:是的,这只是一个概念,需要一些特定方式。但不,资源不会被捆绑在一个大文件中提供给用户,而是根据需求进行加载。 - jayarjo

7
您需要使用webpack(或其他打包工具)。当代码被打包后,它不会被执行,因此编译器无法知道应该遵循哪个逻辑分支(app_1还是app_2)。因此,您需要进入打包工具的逻辑以实现您的目标。然而,这并没有看起来那么可怕,因为webpack内置了这种能力(无需第三方...)。我建议您尝试使用webpack.providePlugin。

(https://webpack.js.org/plugins/provide-plugin)

或其同级 DefinePlugin

(https://webpack.js.org/plugins/define-plugin)

我害怕这些例子是我头脑中临时想出来的,所以第一次执行时很不可能成功。
例子:
两者都需要提供程序模块...
// in path/provider.js

module.exports = {
  live: '/path/to/live/image',
  dev: '/path/to/dev/image'
}

提供插件示例
// in webpack

new webpack.ProvidePlugin({
    imagePath: [
      'path/provider',         // the file defined above
      process.env.ENVIRONMENT  // either 'dev' or 'live'
    ]
  }),

// in code

export default function Test() {
  return (
      <img src={imagePath} alt="" />
  );
}

定义插件示例:
// in webpack

new webpack.DefinePlugin({
  'process.env.ENVIRONMENT': JSON.stringify(process.env.ENVIRONMENT)
});

// in code

var providers = require('path/provider'); // same path provider as above

export default function Test() {
  return (
      <img src={providers[process.env.ENVIRONMENT]} alt="" />
  );
}


在这两种情况下,捆绑器被迫在编译时将您的变量折叠为实际文字值 - 在捆绑之前。由于现在您已将逻辑路径折叠为单个选项,因此它现在可以自由地仅捆绑相关资产。

1
好的。但是提问者提到他不想碰webpack。 - jayarjo
1
Webpack还不错,特别是对于一些简单的更改。我会看一下的。 - jones

3

你不能使用 CRA默认设置 来实现这一点。

因为如果你的 动态 require 或者 动态 import 路径不是 静态 的,webpack 将无法确定哪些资产应该包含在最终构建文件夹中,因此它会从 ./src 文件夹中获取所有内容,并将它们都放入到你的 build 文件夹中。


据我所知,将文件移动到 build 文件夹中并不是问题,但是打包它们(转换为 dataUrls 并包含到最终的 JS 文件中)就是问题。 - jayarjo
它将把这些附加文件拆分为单独的块。 - Joseph

0

有一种方法可以使用默认的CRA设置来完成它

您可以在.env文件中添加类似以下内容:

REACT_APP_SKIN=1

将您的皮肤资产放在public/css1、public/css2等文件夹中。 然后使用以下代码将它们包含在public/index.html中:
<link href="/css%REACT_APP_SKIN%/theme.css" rel="stylesheet">

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接