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

jon*_*nes 11 javascript reactjs webpack es6-modules create-react-app

使用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="" />
  );
}
Run Code Online (Sandbox Code Playgroud)

但是,无论如何,这都会捆绑我的所有资产。

它将加载正确的映像,但仍将所有文件捆绑在一起,形成最终版本。

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

我是否被迫触摸webpack配置,如果是的话,我应该改变什么?还是有另一种方法?

jay*_*rjo 7

In the days when React didn't exist we didn't put assets into our JS files. We let the CSS to decide, what assets to load for what selectors. 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 and code splitting. All you need to do is use parenthesized version of the import() statement instead of the regular one. import() takes in a request string as usual and returns a 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>   
  ); 
}
Run Code Online (Sandbox Code Playgroud)

After:

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

function MyComponent() {
  return (
    <div>
      <OtherComponent />
    </div>
  );
}
Run Code Online (Sandbox Code Playgroud)

Notice how function MyComponent part is identical.

For those wondering if it is tied to CRA or React, it's not. It's a generic concept that can be used in vanilla JavaScript.


Abu*_*fia 6

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). 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...)

I would look into using webpack.providePlugin

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

or its sibling DefinePlugin

(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'
}
Run Code Online (Sandbox Code Playgroud)

Provide Plugin Example

// in webpack

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

export default function Test() {
  return (
      <img src={imagePath} alt="" />
  );
}
Run Code Online (Sandbox Code Playgroud)

Define Plugin example:

// in webpack

new webpack.DefinePlugin({
  'process.env.ENVIRONMENT': JSON.stringify(process.env.ENVIRONMENT)
});
Run Code Online (Sandbox Code Playgroud)
// in code

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

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

Run Code Online (Sandbox Code Playgroud)

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.