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

我正在使用create-react-app。我试图从我的src/components内的文件中调用公共文件夹中的图像。我收到这个错误信息。

./src/components/website_index.js Module not found: You attempt to import ../../public/images/logo/WC-BlackonWhite.jpg 在项目src/目录之外。国外的相对进口 Src /不支持。您可以将它移动到src/,或者添加一个 从项目的node_modules/.

到它的符号链接

import logo from '../../public/images/logo_2016.png'; & lt; img className = " Header-logo " src ={标志}alt = "标志" /祝辞< /代码> < / p >

我读过很多东西,说你可以做一个导入的路径,但这仍然不是为我工作。任何帮助都将不胜感激。我知道有很多这样的问题,但他们都告诉我导入标志或形象,所以很明显,我在大局中遗漏了一些东西。

416790 次浏览

你需要移动WC-BlackonWhite.jpg到你的src目录。public目录用于将直接链接到HTML中的静态文件(例如favicon),而不是您将直接导入到您的bundle中的东西。

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

  1. 使用react-create-app提供的模块system (import x from y),并将其捆绑到你的JS中。将图像放置在src文件夹中。

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

来源:create-react-app

这是create-react-app开发者添加的特殊限制。它在ModuleScopePlugin中实现,以确保文件驻留在src/中。该插件确保从应用程序的源目录导入的相对文件不会到达应用程序的外部。

除了使用eject和修改webpack配置,没有任何官方方法可以禁用此功能。

但是,大多数功能及其更新都隐藏在创建-反应-应用程序系统的内部。如果你使eject,你将没有更多的新功能和它的更新。因此,如果你还没有准备好管理和配置应用程序,包括配置webpack等-不要做eject操作。

玩现有的规则-移动资产到src或使用基于public文件夹url不导入。


然而,除了eject之外,还有许多非官方的解决方案,这些解决方案基于 重新连接,允许你在没有eject的情况下以编程方式修改webpack配置。但是删除——ModuleScopePlugin插件不好——这失去了一些保护,并且没有添加一些在src中可用的特性。ModuleScopePlugin被设计为支持多个文件夹。

更好的方法是添加完全工作的附加目录,类似于src,也受ModuleScopePlugin保护。这可以使用react-app-alias来完成


无论如何,不要从public文件夹导入-它将在build文件夹中复制,并将通过两个不同的url(并且有不同的加载方式)可用,这最终会恶化包的下载大小。

src文件夹导入是更可取的,它有很多优点。所有内容都将通过webpack打包到绑定包中,包含最优规模最佳装载效率块。

如果您只需要导入单个文件,如README。Md或package。json,那么它可以显式地添加到ModuleScopePlugin()

配置/ 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()
]),
]
}
}

此限制确保所有文件或模块(导出)都在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. 或从./node_modules/react-scripts/config/webpack.config.dev.js中注释/删除const ModuleScopePlugin = require('react-dev-utils/ModuleScopePlugin');

注:注意喷射的后果。

如果你的图片在公共文件夹中,那么你应该使用

"/images/logo_2016.png"

在你的<img> src中而不是导入

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

这是可行的

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

最好的解决方案是fork react-scripts,这实际上在官方文档中提到过,参见:驱逐的替代方案

react-app-rewired可以用来删除插件。这样你就不用弹射了。

按照npm包页面上的步骤(安装包并翻转包中的调用)。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,但保留其余部分,并消除了弹出的必要性。

你不需要弹出,你可以用布告图书馆修改react-scripts配置

这样就可以了:

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


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


return config;
};

我认为卢卡斯巴赫解使用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;
};

有一些答案提供了react-app-rewired的解决方案,但customize-cra包含了一个更优雅的removeModuleScopePlugin() 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()

安装这两个包

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


如果你需要多个修改,比如当使用蚂蚁设计时,你可以像这样组合多个函数:

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


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

再加上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;
}
}
};

我之前的解决方案适用于Webpack 4,但不适用于Webpack 5。在浏览了从那时起积累的解决方法之后,我发现下面的方法非常简单(而且似乎是可扩展的)。

import { CracoAliasPlugin } from 'react-app-alias';


const cracoConfig = {
plugins: [
{
plugin: CracoAliasPlugin,
options: {
alias: { '~': './' },
},
},
],
}

然后像这样导入:

import whatever from '~/<path-to-file>';

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

公共文件夹内的图像

  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扩展

您可以尝试使用simlinks,但是相反。

React不会遵循simlink,但是你可以将一些东西移动到源目录,并创建一个simlink到它。

在项目的根目录中,我有一个节点服务器目录,其中有几个模式文件。我想在前端使用它们,所以我:

  • 移动文件/src
  • 在终端,我cd到哪里模式文件属于服务器
  • ln -s SRC_PATH_OF_SCHEMA_FILE

这为react提供了它所需要的东西,node也很乐意通过simlinks包含文件。

在制作《Truffle》时,我不得不克服同样的问题。解决方案如下:

由于Create-React-App的默认行为不允许从src文件夹外导入文件,我们需要将build文件夹中的合同带到src内。我们可以在每次编译合同时复制并粘贴它们,但更好的方法是简单地配置Truffle将文件放在那里。

在“truffle-config.js”文件中,替换如下内容:

const path = require("path");


module.exports = {
contracts_build_directory: path.join(__dirname, "client/src/contracts")
};

我不知道这是否对你有帮助,但我知道当我在Truffle中遇到同样的问题时,我发现了你的问题,这可能会帮助其他人。

在我的项目中遇到了同样的问题,并在官方的create-react-app文档中发现了这个:https://create-react-app.dev/docs/using-the-public-folder/

有一个“逃生口”;在模块系统外添加一个资产:

如果你把一个文件放入公用文件夹,它将不会被处理 webpack。相反,它将原封不动地复制到构建文件夹中。来 在公用文件夹中引用资产时,需要使用环境 变量PUBLIC_URL.

下面是他们提供的一个例子:

render() {
// Note: this is an escape hatch and should be used sparingly!
// Normally we recommend using `import` for getting asset URLs
// as described in “Adding Images and Fonts” above this section.
return <img src={process.env.PUBLIC_URL + '/img/logo.png'} />;
}

如果你想从公共中访问CSS文件,你可能会遇到错误源目录外

或者,你也可以在index . html中链接这个文件,它也位于公共目录中。

<link rel="stylesheet" href="App.css">

下面是一个在简单情况下工作良好的替代方案(使用fsncp)。在开发过程中,保持一个脚本运行,以监视/src之外的共享文件夹的更改。当进行更改时,脚本可以自动将共享文件夹复制到项目中。下面是递归地查看单个目录的示例:


// This should be run from the root of your project


const fs = require('fs')
const ncp = require('ncp').ncp;


ncp.limit = 16


// Watch for file changes to your shared directory outside of /src
fs.watch('../shared', { recursive: true }, (eventType, filename) => {
console.log(`${eventType}: ${filename}`)


// Copy the shared folder straight to your project /src
// You could be smarter here and only copy the changed file
ncp('../shared', './src/shared', function(err) {
if (err) {
return console.error(err);
}


console.log('finished syncing!');
});
})
这是一个相对导入的问题,这可能是因为我们使用了“create-react-app project”;命令,该命令形成一个名为project的目录,其中包含node_modules文件夹和其他几个公共和SRC文件夹中的文件。 create-react-app命令设置了一个限制,即我们不能从src外部导入任何东西

我的问题:

  1. 我必须导入在src文件夹外的node_modules文件夹中创建的react-bootstrap css文件。
  2. 我使用import "../node_modules/bootstrap/dist/css/bootstrap.min. cssquot;;但是我在终端上得到了错误。
  3. 我发现我可以创建一个新的react应用程序,并遵循从a到G的解决步骤,以解决这个问题。
< p >解决方案: A)创建一个新的react应用,使用Create -react-app new

B) cd new

C)运行以下命令:&;npm install react-bootstrap bootstrap@4.6.0&;(没有""双引号)

D)在你的react文件中导入bootstrap: D.1)导入“../node_modules/bootstrap/dist/css/bootstrap.min.css"; 或 D.2)import Button from "react-bootstrap/Button";

E)在你的react文件中创建一个bootstrap元素,如Button或任何东西,用于d .;btn btn-success;>引导& lt;/ button> 或 D.2) <按钮变体=“primary"比;引导& lt;/ Button> < / p >

F)在终端:cd src

G)在终端:npm启动,

这一次它将被成功编译。

< p >推理: 当我按顺序执行步骤A到G时,我可以看到react-bootstrap终于开始工作了,这一次我没有得到任何错误。 (我想到这个解决方案是因为:

  1. 我使用npm install "@material-ui/icon "它被安装在src外部的node_modules文件夹中。
  2. 在react文件中,我使用了import Add from "@material-ui/icons/Add"
  3. 和材质ui图标对我来说工作得很好, 但这里我们也从src外部导入,从node_modules..一切都很正常。为什么这次从src外部导入没有错误)
  4. 这就是为什么我刚刚创建了一个新的react应用程序,并遵循解决方案步骤a到G。

复制-粘贴Typescript解决方案

(例如,这适用于CRA/TS堆栈,与CRA/JS相比,它需要额外的步骤。解决方案本身没有类型。)

将所需的路径添加到ModuleScopePlugin,而不是直接删除插件。

下面的代码使用craco,但是应该很容易用于react-app-rewired或类似的解决方案。你只需要找到你有一个webpackConfig对象的位置(react-app-rewired: module.exports.webpack在你的config-overrides.js中),并将它传递给提供的函数。

craco.config.js

const path = require("path");
const enableImportsFromExternalPaths = require("./src/helpers/craco/enableImportsFromExternalPaths");


// Paths to the code you want to use
const sharedLibOne = path.resolve(__dirname, "../shared-lib-1/src");
const sharedLibTwo = path.resolve(__dirname, "../shared-lib-2/src");


module.exports = {
plugins: [
{
plugin: {
overrideWebpackConfig: ({ webpackConfig }) => {
enableImportsFromExternalPaths(webpackConfig, [
// Add the paths here
sharedLibOne,
sharedLibTwo,
]);
return webpackConfig;
},
},
},
],
};

助手/名/ enableImportsFromExternalPaths.js

const findWebpackPlugin = (webpackConfig, pluginName) =>
webpackConfig.resolve.plugins.find(
({ constructor }) => constructor && constructor.name === pluginName
);


const enableTypescriptImportsFromExternalPaths = (
webpackConfig,
newIncludePaths
) => {
const oneOfRule = webpackConfig.module.rules.find((rule) => rule.oneOf);
if (oneOfRule) {
const tsxRule = oneOfRule.oneOf.find(
(rule) => rule.test && rule.test.toString().includes("tsx")
);


if (tsxRule) {
tsxRule.include = Array.isArray(tsxRule.include)
? [...tsxRule.include, ...newIncludePaths]
: [tsxRule.include, ...newIncludePaths];
}
}
};


const addPathsToModuleScopePlugin = (webpackConfig, paths) => {
const moduleScopePlugin = findWebpackPlugin(
webpackConfig,
"ModuleScopePlugin"
);
if (!moduleScopePlugin) {
throw new Error(
`Expected to find plugin "ModuleScopePlugin", but didn't.`
);
}
moduleScopePlugin.appSrcs = [...moduleScopePlugin.appSrcs, ...paths];
};


const enableImportsFromExternalPaths = (webpackConfig, paths) => {
enableTypescriptImportsFromExternalPaths(webpackConfig, paths);
addPathsToModuleScopePlugin(webpackConfig, paths);
};


module.exports = enableImportsFromExternalPaths;

取自在这里在这里🙏

这可以直接完成,而不使用公共文件夹的路径。 你可以这样写

<img src="/images/image-name" alt=""/>

这是因为我们没有在浏览器中使用App.js。由于index.html是在浏览器本身执行的,图像的路径已经在包含index.html文件的公共文件夹中

如果你想使用CSS设置背景图像。因此,您必须使用本地主机的URL设置图像并添加图像的路径。请看下面的例子。

.banner {
width: 100%;
height: 100vh;
background-image: url("http://localhost:3000/img/bg.jpg");
background-size: cover;
background-repeat: no-repeat;
}

因此,显然,使用Create-react-app将限制你访问项目根目录以外的图像——通常是src

一个快速的修复方法是将你的图像从public文件夹移动到src文件夹,你就可以开始了。
所以你的代码将像这样,例如:

background-image: URL('/src/images/img-2.jpg');

把@Flaom写的评论放在这里,标记为回复答案,这实际上挽救了生命:

“这怎么是公认的答案?”通过简单地设置NODE_PATH=./src/..在。env文件中。通过这样做,你可以从src文件夹外导入,而不用经历弹出应用程序的痛苦。

  • Flaom

编辑添加了一些更多的信息@cigien请求。

上面所有的答案都很好地描述了为什么当我们用create-react-app创建react应用程序时,我们不能使用公共文件夹中的图像。我自己也有这个问题,读了所有这些答案,我意识到,答案说的是“hack”。该应用程序以删除限制我们的模块。有些答案甚至没有撤销选项。为了“培训”;应用程序是可以的。

就我个人而言,我不希望在我自己的项目中添加改变应用概念的解决方案,特别是在商业项目中。@Flaom解决方案是最简单的,如果将来有任何变化,它可以被另一个解决方案取代。它没有任何风险,可以随时移除,是最简单的。

我可以通过“copy”导入src/之外的文件;file:作为本地依赖项的外部文件。

"dependencies": {
"@my-project/outside-dist": "file:./../../../../dist".
}

然后

import {FooComponent} from "@my-project/outside-dist/components";

不需要ejectreact-app-rewired或其他第三方解决方案。

这是我的代码:

import React from 'react';


import './Navbar.scss';
import {images} from '../../constants';
const Navbar = () => {
return (
<nav>
<div>
< img src = {images.logo} alt = "logo" />


</div>


</nav>
);
}


export default Navbar;

也改了:

import React from 'react';


import './Navbar.scss';
import {images} from '././constants';
const Navbar = () => {
return (
<nav>
<div>
< img src = {images.logo} alt = "logo" />


</div>


</nav>
);
}


export default Navbar;

它成功了!我越来越擅长修复bug,哈哈。

如果你的文件位于公共文件夹,如果你想导入它而不弹出或不使用react-app-rewired,那么在这种情况下,你可以通过域名和文件的路径访问文件,并使用axios。 示例:在公共文件夹中有一个名为favico.ico的字体文件。你想把它导入到src中的一个文件中。你

?

axios.get('example.com/favico.ico').then(() => {
// here you can access this file.
})

在上面的例子中example.com是域。如果您有不同的环境,如本地主机,登台,生产,那么在这种情况下,域名是不同的。 因此,要获得favico.ico,您可以使用以下逻辑

axios.get(`${window.location.origin}/favico.ico`).then(() => {
// here you can access this file.
})
在上面的例子中,window.location.origin给你当前域的意思是,如果你在本地运行你的代码,它会给你http://localhost:{portnumber}, 如果您的代码运行在生产域中,并且生产域是example.com,那么它将为您提供"example.com"。因此,使用这种模式,您可以访问位于公共文件夹中的资产

这对我来说很有效,不需要安装/更改任何东西

上下文:当我试图使用yarn run build生成一个构建时,我得到了这个错误

我在yarn run build的工作和失败之间所做的事情

我将ant设计更新到最新的稳定版本(v4.23.5)。

注:我非常相信这与这个版本无关。我提一下只是为了补充一些细节。

  • 这个回答解决了我的问题。但是我没有更改任何访问src目录之外内容的导入。
  • 更改包括更新的包。json,纱线。lock,新的Antd实现(主要是道具的变化)。
  • 为什么build command坏了/为什么回答工作是没有意义的。

解决办法

因为所有的变化都与包装有关。json, yarn.lock。我删除了node_modules并清洁安装了所有的包。

运行

yarn

npm install