简体   繁体   English

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

[英]Conditionally import assets in create-react-app

Is it possible to conditionally import assets when creating a React app using create-react-app?是否可以在使用 create-react-app 创建 React 应用程序时有条件地导入资产? I'm aware of the require syntax - example:我知道 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="" />
  );
}

This however bundles all my assets no matter what.然而,无论如何这都会捆绑我的所有资产。

It will load the proper image, but it will still bundle all the files together in the final build.它将加载正确的图像,但它仍将在最终构建中将所有文件捆绑在一起。

When I look in the dev tools for the final build, I can see all the assets there even though I only wanted to load the assets for app_1 .当我查看最终构建的开发工具时,我可以看到那里的所有资产,即使我只想加载app_1的资产。

Am I forced to touch the webpack config, if so, what should I change?我是否被迫接触 webpack 配置,如果是这样,我应该改变什么? or is there another way?还是有另一种方法?

In the days when React didn't exist we didn't put assets into our JS files.在 React 不存在的时代,我们没有将资产放入我们的 JS 文件中。 We let the CSS to decide, what assets to load for what selectors.我们让 CSS 来决定,为哪些选择器加载哪些资产。 Then you could simply switch a corresponding class on or off for a corresponding element ( or even the whole page ) and viola it changes color, background, or even a form.然后你可以简单地为相应的元素(甚至整个页面)打开或关闭相应的类,中提琴它会改变颜色、背景甚至表单。 Pure magic!纯粹的魔法!

Ah.啊。 What times these were!这是什么时候!

All above is true and I do not understand why would anyone do or recommend doing it differently.以上都是真实的,我不明白为什么有人会这样做或建议以不同的方式这样做。 However if you still want to do it ( for any reason ) - you can!但是,如果您仍然想这样做(出于任何原因)-您可以! Latest create-react-app comes with out-of-the-box support for lazy loading of arbitrary components via dynamic importing andcode splitting .最新的create-react-app带有开箱即用的支持,可通过动态导入和代码拆分延迟加载任意组件。 All you need to do is use parenthesized version of the import() statement instead of the regular one.您需要做的就是使用带括号的import()语句而不是常规语句。 import() takes in a request string as usual and returns a Promise. import()像往常一样接收请求字符串并返回一个 Promise。 That's it.就是这样。 Source code of the dynamicaly requested component won't be bundled in, but instead stored in separate chunks to be loaded on demand.动态请求的组件的源代码不会捆绑在一起,而是存储在单独的块中,以便按需加载。

Before:前:

import OtherComponent from './OtherComponent';

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

After:后:

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

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

Notice how function MyComponent part is identical.请注意function MyComponent部分是如何相同的。

For those wondering if it is tied to CRA or React, it's not.对于那些想知道它是否与 CRA 或 React 相关的人来说,事实并非如此。 It's a generic concept that can be used in vanilla JavaScript .这是一个可以在 vanilla JavaScript 中使用的通用概念。

You will need to use webpack (or other bundler.) The code is not being run when it's bundled, so the compiler has no way of knowing which branch of logic to follow (app_1 or app_2).需要使用 webpack(或其他打包程序)。代码在打包时并未运行,因此编译器无法知道要遵循哪个逻辑分支(app_1 或 app_2)。 Therefore you have to get into the bundler's logic in order to achieve your goal.因此,您必须进入打包程序的逻辑才能实现您的目标。

However, this isn't as scary as it seems since webpack has built in capability to do this (no 3rd parties required...)然而,这并不像看起来那么可怕,因为 webpack 内置了执行此操作的功能(不需要 3rd 方......)

I would look into using webpack.providePlugin我会考虑使用 webpack.providePlugin

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

or its sibling DefinePlugin或者它的同级DefinePlugin

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

(I'm afraid these examples are off the top of my head, so it's very unlikely they'll work on first pass.) (我担心这些例子超出了我的脑海,所以它们不太可能在第一次通过时起作用。)

Examples:例子:

Both will require a provider module...两者都需要一个提供者模块......

// in path/provider.js

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

Provide Plugin Example提供插件示例

// 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="" />
  );
}

Define Plugin example:定义插件示例:

// 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="" />
  );
}

In both cases the bundler is forced to collapse your variable to an actual literal value at compile time - before bundling has taken place.在这两种情况下,捆绑器都被迫在编译时将您的变量折叠为实际文字值 - 在捆绑发生之前。 Since you have now collapsed the logical path down to a single option, it is now free to only bundle the relevant assets.由于您现在已将逻辑路径折叠为单个选项,因此现在可以只捆绑相关资产。

You can't do this with default CRA settings .您无法使用默认的 CRA 设置执行此操作。

Because if your dynamic require or dynamic import path is not static , webpack won't be able to determine which assets to include in the final build folder, therefore, it will grab everything from your ./src folder, and put them all to your build folder.因为如果您的动态 require动态导入路径不是static ,则 webpack 将无法确定将哪些资产包含在最终构建文件夹中,因此,它将从您的./src文件夹中抓取所有内容,并将它们全部放入您的build文件夹。

There is a way to do it with default CRA settings有一种方法可以使用默认的 CRA 设置

You can add to .env something like你可以添加到 .env 类似的东西

REACT_APP_SKIN=1

Put your skin assets in public/css1, public/css2 etc. And include them in public/index.html using code like将您的皮肤资产放在 public/css1、public/css2 等中。并使用如下代码将它们包含在 public/index.html 中

<link href="/css%REACT_APP_SKIN%/theme.css" rel="stylesheet">

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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