src目录外部的create-react-app导入限制


149

我正在使用create-react-app。我正在尝试从文件夹中的文件中调用公用文件夹中的图片src/components。我收到此错误消息。

./src/components/website_index.js找不到模块:您试图导入属于项目src /目录之外的../../public/images/logo/WC-BlackonWhite.jpg。不支持src /以外的相对导入。您可以将其移动到src /中,也可以从项目的node_modules /中向其添加符号链接。

import logo from '../../public/images/logo_2016.png'; <img className="Header-logo" src={logo} alt="Logo" />

我读过很多东西,说您可以导入路径,但对我来说仍然行不通。任何帮助将不胜感激。我知道有很多这样的问题,但是它们都告诉我要导入徽标或图像,因此很明显我在全局图中缺少某些内容。


2
您需要../public/images/logo_2016.png上两次,首先从components文件夹中移出,然后从src文件夹中移出。
克里斯·G

./src/components/website_index.js找不到模块:您试图导入属于项目src /目录之外的../../public/images/logo/WC-BlackonWhite.jpg。不支持src /以外的相对导入。您可以将其移动到src /中,也可以从项目的node_modules /中向其添加符号链接。
大卫·布里顿

我的评论假定您的public文件夹直接位于您的src文件夹内。您的无评论评论以旧路径开头,../..因此不确定您的意思是什么?
克里斯·G

3
没有任何一个公共部门与src处于同一级别
David Brierton

它们的意思是“或从项目的node_modules /向其添加符号链接”?
朱哈

Answers:


126

这是create-react-app开发人员添加的特殊限制。的实现是ModuleScopePlugin为了确保文件位于中src/。该插件可确保从应用程序源目录的相对导入不会到达其外部。

您只能在eject运行create-react-app项目后才能禁用此功能。

大多数功能及其更新都隐藏在create-react-app系统的内部。如果您这样做eject,将不再具有某些功能及其更新。因此,如果您还没有准备好管理和配置用于配置Webpack等的应用程序-请勿执行eject操作。

按照现有规则播放(移至src)。但现在你可以知道如何删除限制:eject和删除ModuleScopePlugin从的WebPack配置文件


create-react-app v0.4.0开始NODE_PATH环境变量允许为绝对导入指定路径。并且由于不赞成使用v3.0.0 NODE_PATH,而建议将其设置baseUrljsconfig.jsontsconfig.json

绝对进口允许使用import App from 'App'代替import App from './App'相对于在基本URL指定的值。

此功能对monorepos或其他配置问题特别有用,但不适用于从public文件夹导入图像或其他任何内容。

public文件夹的内容将放置在build文件夹中,并通过相对URL提供。另外,所有导入的内容都将由webpack处理,也将放置在build文件夹中。

如果您从public文件夹中导入某些内容,则该内容可能会在该build文件夹中重复出现,并且可以通过两个不同的url(或使用不同的加载方式)使用,这最终会使软件包下载大小变大。

从src文件夹导入是可取的,并且具有一些优点。所有内容都将由webpack打包成具有最佳大小最佳加载效率的块。


有中间解决方案,即重新布线系统,它允许您以编程方式修改webpack配置。但除去ModuleScopePlugin插件是不是一个很好的解决方案; 最好添加完全可用的类似于的其他目录src

当前,除根文件夹create-react-app外,不支持其他目录src。这可以使用react-app-rewire-alias完成


3
如果您在./src中创建符号链接,然后从那里导入-构建将无法正常工作(babel插件不会转换符号链接文件夹中的源代码)。因此,有了这个限制,并且在src下没有符号链接,您实际上被安置在“与其他项目无共享代码”的监狱里(除非您选择完全从CRA移出/退出)
VP

2
@VP,但错误显示为“从项目的node_modules /向其添加符号链接。”,这行不通吗?
adrianmc '18

create-react-app对于那些不想弹出webpack配置的用户,他们应该标记以禁用此功能。就我个人而言,我总是会弹出,但是有些人不舒服,但对令人讨厌的webpacks感到困惑。
TetraDev

2
@adrianmc。从项目的node_modules添加符号链接不起作用,因为许多项目使用的共享组件/代码不是node_modules。例如,我在React Native和React native Web之间共享代码。这些“片段”不是node_modules,而这两个项目实际上具有不同的node_modules。
VP

1
这是如何被接受的答案?只需NODE_PATH=./src/...env文件中进行设置,就可以消除这种伪造的限制。这样,您可以从src文件夹外部导入,而无需经历与弹出应用程序相关的麻烦。
Flaom

47

软件包react-app-rewired可以用来删除插件。这样,您不必弹出。

遵循npm软件包页面上的步骤(安装软件包并翻转package.json文件中的调用),并使用config-overrides.js与此类似的文件:

const ModuleScopePlugin = require('react-dev-utils/ModuleScopePlugin');

module.exports = function override(config, env) {
    config.resolve.plugins = config.resolve.plugins.filter(plugin => !(plugin instanceof ModuleScopePlugin));

    return config;
};

这将从使用过的WebPack插件中删除ModuleScopePlugin,但其余部分保持原样,并消除了弹出的必要性。


5
好答案。您可以react-app-rewired通过[ github.com/arackaf/customize-cra](customize-cra)进一步利用。然后,配置将使用just babelInclude([path.resolve('src'), path.resolve('../common')])removeModuleScopePlugin()
ivosh

您能确认我应将此代码段放在哪里吗?
bijayshrestha

1
@bijayshrestha阅读react-app-rewired项目的自述文件,其中说明了如何进行设置。在安装过程中,您将创建一个config-overrides.js文件,可以在其中放置代码。
卢卡斯·巴赫

删除ModuleScopePlugin插件不是一个好主意。最好添加完全正常工作的其他目录,类似于src使用react-app-
rewire

1
此解决方案是否适用于Typescript?我无法在TS上使用它。
user3053247

27

为他人的答案提供更多信息。关于如何将.png文件交付给用户,您有两个选择。文件结构应符合您选择的方法。这两个选项是:

  1. 使用import x from yreact-create-app随附的模块系统()并将其与JS捆绑在一起。将图像放在src文件夹中。

  2. public文件夹提供服务,然后让Node提供文件。create-react-app显然还带有环境变量,例如<img src={process.env.PUBLIC_URL + '/img/logo.png'} />;。这意味着您可以在React应用程序中引用它,但仍然可以通过Node来提供它,而浏览器会在常规GET请求中单独要求它。

资料来源:create-react-app


建议2正是导致我以下错误的原因:找不到模块:您试图导入./../../../public/CheersBar-Drinks-147.jpg,该项目不在项目src /目录下。不支持src /以外的相对导入。您可以将其移动到src /中,也可以从项目的node_modules /中向其添加符号链接。
阿莫斯(Amos Long)

25

如果图像位于公用文件夹中,则应使用

"/images/logo_2016.png"

在您<img> src而不是导入

'../../public/images/logo_2016.png'; 

这会起作用

<img className="Header-logo" src="/images/logo_2016.png" alt="Logo" />

2
不为我工作。得到相同的消息-'在项目src /目录之外。不支持src /以外的相对导入。
Arkady

您的答案是正确的IMO,但我还是想澄清一下,您是在谈论路径<img src="...">,而不是输入路径
Dmitry Yudakov

1
这正是我想要的!我只是在我的CRA的“ src”目录中添加了一个“图像”文件夹,然后便可以使用<img src="/images/logo.png" alt="Logo" />
Amos Long,

12

您需要WC-BlackonWhite.jpg进入src目录。该public目录用于将直接在HTML中链接的静态文件(例如favicon),而不是要直接导入捆绑包中的内容。


我见过其他人这样做。这就是为什么我认为这是正确的方式
大卫Brierton

@DavidBrierton:在最新版本的create-react-app中可能已添加了此预防措施。
乔·克莱

8

有一些答案可以为提供解决方案react-app-rewired,但是提供customize-craremoveModuleScopePlugin()一个更优雅的特殊API。(这是相同的解决方案,但由customize-cra程序包抽象出来。)

npm i --save-dev react-app-rewired customize-cra

package.json

"scripts": {
    - "start": "react-scripts start"
    + "start": "react-app-rewired start",
    ...
},

config-overrides.js

const { removeModuleScopePlugin } = require('customize-cra')

module.exports = removeModuleScopePlugin()

1
你救了我的一天!
lmiguelvargasf

5

使用Craco将其删除:

module.exports = {
  webpack: {
    configure: webpackConfig => {
      const scopePluginIndex = webpackConfig.resolve.plugins.findIndex(
        ({ constructor }) => constructor && constructor.name === 'ModuleScopePlugin'
      );

      webpackConfig.resolve.plugins.splice(scopePluginIndex, 1);
      return webpackConfig;
    }
  }
};

投票给疯子!Craco支持CRA 3.x
Roman Podlinov

4

此限制可确保所有文件或模块(导出)都在src/目录内,实现在中./node_modules/react-dev-utils/ModuleScopePlugin.js,在以下代码行中。

// Resolve the issuer from our appSrc and make sure it's one of our files
// Maybe an indexOf === 0 would be better?
     const relative = path.relative(appSrc, request.context.issuer);
// If it's not in src/ or a subdirectory, not our request!
     if (relative.startsWith('../') || relative.startsWith('..\\')) {
        return callback();
      }

您可以通过以下方式删除此限制

  1. 要么更改这段代码(不推荐)
  2. eject然后取出ModuleScopePlugin.js从目录。
  3. 或评论/ const ModuleScopePlugin = require('react-dev-utils/ModuleScopePlugin');从中删除./node_modules/react-scripts/config/webpack.config.dev.js

PS:当心弹出的后果。


谢谢@SurajRao,我将其移到了这里。我希望更好。
its4zahoor

@PattycakeJr是的,这就是为什么我在下面告诉我要避免弹出后果的原因。
its4zahoor

3

安装这两个软件包

npm i --save-dev react-app-rewired customize-cra

package.json

"scripts": {
    - "start": "react-scripts start"
    + "start": "react-app-rewired start"
},

config-overrides.js

const { removeModuleScopePlugin } = require('customize-cra');

module.exports = function override(config, env) {
    if (!config.plugins) {
        config.plugins = [];
    }
    removeModuleScopePlugin()(config);

    return config;
};

2

您不需要弹出,可以react-scripts使用rescripts库修改配置

然后可以这样:

module.exports = config => {
  const scopePluginIndex = config.resolve.plugins.findIndex(
    ({ constructor }) => constructor && constructor.name === "ModuleScopePlugin"
  );

  config.resolve.plugins.splice(scopePluginIndex, 1);

  return config;
};

2

我认为Lukas Bach解决方案使用react-app- rewired来修改webpack配置是一个不错的方法,但是,我不会排除整个ModuleScopePlugin,而是将可以导入src外部的特定文件列入白名单:

config-overrides.js

const ModuleScopePlugin = require("react-dev-utils/ModuleScopePlugin");
const path = require("path");

module.exports = function override(config) {
  config.resolve.plugins.forEach(plugin => {
    if (plugin instanceof ModuleScopePlugin) {
      plugin.allowedFiles.add(path.resolve("./config.json"));
    }
  });

  return config;
};

到目前为止,这是最好的解决方案。
adi518

2

公用文件夹中的图像

  use image inside html extension
  <img src="%PUBLIC_URL%/resumepic.png"/>

  use image inside  js extension
  <img src={process.env.PUBLIC_URL+"/resumepic.png"}/>
  • 在js Extension中使用图片

1

如果只需要导入一个文件,例如README.md或package.json,则可以将其显式添加到ModuleScopePlugin()

config / paths.js

const resolveApp = relativePath => path.resolve(appDirectory, relativePath);
module.exports = {
  appPackageJson: resolveApp('package.json'),
  appReadmeMD:    resolveApp('README.md'),
};

config / webpack.config.dev.js + config / webpack.config.prod.js

module.exports = {
  resolve: {
    plugins: [
      // Prevents users from importing files from outside of src/ (or node_modules/).
      // This often causes confusion because we only process files within src/ with babel.
      // To fix this, we prevent you from importing files out of src/ -- if you'd like to,
      // please link the files into your node_modules/ and let module-resolution kick in.
      // Make sure your source files are compiled, as they will not be processed in any way.
      new ModuleScopePlugin(paths.appSrc, [
          paths.appPackageJson,
          paths.appReadmeMD         // README.md lives outside of ./src/ so needs to be explicitly included in ModuleScopePlugin()
      ]),
    ]
  }
}

3
这需要先从create-react-app弹出,不是吗?
博·史密斯,


1

如果您需要进行多种修改(例如使用ant design时),则可以将多种功能组合如下:

const {
  override,
  removeModuleScopePlugin,
  fixBabelImports,
} = require('customize-cra');

module.exports = override(
  fixBabelImports('import', {
    libraryName: 'antd',
    libraryDirectory: 'es',
    style: 'css',
  }),
  removeModuleScopePlugin(),
);

1

除Bartek Maciejiczek的答案外,Craco的外观也是如此:

const ModuleScopePlugin = require("react-dev-utils/ModuleScopePlugin");
const path = require("path");

module.exports = {
  webpack: {
    configure: webpackConfig => {
      webpackConfig.resolve.plugins.forEach(plugin => {
        if (plugin instanceof ModuleScopePlugin) {
          plugin.allowedFiles.add(path.resolve("./config.json"));
        }
      });
      return webpackConfig;
    }
  }
};

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.