簡體   English   中英

src 目錄之外的 create-react-app 導入限制

[英]The create-react-app imports restriction outside of src directory

我正在使用 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" />

我讀過很多東西說你可以對路徑進行導入,但這仍然對我不起作用。 任何幫助將不勝感激。 我知道有很多這樣的問題,但他們都告訴我要導入徽標或圖像,所以很明顯我在大局中遺漏了一些東西。

這是 create-react-app 的開發者添加的特殊限制。 它在ModuleScopePlugin實現以確保文件駐留在src/ 該插件確保來自應用程序源目錄的相對導入不會到達它之外。

您可以通過 create-react-app 項目的eject操作禁用此功能(其中一種方法)。

大多數功能及其更新都隱藏在 create-react-app 系統的內部。 如果您進行eject您將不再擁有某些功能及其更新。 因此,如果您還沒有准備好管理和配置包含的應用程序以配置 webpack 等 - 請勿執行eject操作。

按照現有規則進行操作(移至 src)。 但是現在你可以知道如何移除限制:從 webpack 配置文件中eject和移除ModuleScopePlugin


而不是eject有中間解決方案,如再布線,讓您以編程方式修改的WebPack配置沒有eject 但是刪除ModuleScopePlugin插件並不好- 這會失去一些保護並且不會添加src可用的一些功能。

更好的方法是添加類似於src完全可用的附加目錄。 這可以使用react-app-rewire-alias來完成


不要從public文件夾導入 - 這將在build文件夾中復制,並且可以通過兩個不同的 url(或使用不同的加載方式)訪問,這最終會惡化包下載的大小。

src文件夾導入是可取的並且具有優勢。 一切都將被 webpack 打包到具有最佳大小最佳加載效率的塊的包中。

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,但保留其余部分並消除彈出的必要性。

如果您的圖像在公共文件夾中,那么您應該使用

"/images/logo_2016.png"

在你的<img> src而不是導入

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

這將工作

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

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

為其他人的答案提供更多信息。 關於如何將 .png 文件交付給用戶,您有兩種選擇。 文件結構應符合您選擇的方法。 這兩個選項是:

  1. 使用 react-create-app 提供的模塊系統( import x from y )並將其與您的 JS 捆綁在一起。 將圖像放在src文件夾中。

  2. public文件夾提供它,讓 Node 提供文件。 create-react-app 顯然也帶有一個環境變量,例如<img src={process.env.PUBLIC_URL + '/img/logo.png'} />; . 這意味着你可以在你的 React 應用程序中引用它,但仍然通過 Node 提供它,你的瀏覽器在普通的 GET 請求中單獨請求它。

來源: 創建反應應用程序

有一些答案提供了react-app-rewired解決方案,但customize-cra包含一個更優雅的removeModuleScopePlugin() API。 (這是相同的解決方案,但被customize-cra包抽象掉了。)

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

包.json

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

config-overrides.js

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

module.exports = removeModuleScopePlugin()

您需要將WC-BlackonWhite.jpg移動到您的src目錄中。 public目錄用於將直接鏈接到 HTML 中的靜態文件(例如收藏夾圖標),而不是您將直接導入到包中的內容。

我認為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;
};

安裝這兩個包

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

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

此限制確保所有文件或模塊(導出)都在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:注意eject的后果。

公用文件夾內的圖像

  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 擴展中使用圖像

我不得不在 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 遇到同樣問題時發現了您的問題,這可能對其他人有所幫助。

復制粘貼打字稿解決方案

(例如,這將適用於 CRA/TS 堆棧,與 CRA/JS 相比,這需要一個額外的步驟。解決方案本身沒有類型化。)

將所需的路徑添加到ModuleScopePlugin而不是直接刪除插件。

下面的代碼使用craco ,但應該很容易用於react-app-rewired或類似的解決方案。 您只需要找到您擁有webpackConfig對象的位置(react-app- module.exports.webpack : 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;
                },
            },
        },
    ],
};

helpers/craco/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;

取自這里這里🙏

不需要eject,可以用rescripts庫修改react-scripts配置

這將起作用:

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

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

  return config;
};

添加到 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;
        }
      }
    };

如果你只需要導入單個文件,比如 README.md 或 package.json,那么這可以顯式添加到 ModuleScopePlugin()

配置/paths.js

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

配置/webpack.config.dev.js + 配置/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()
      ]),
    ]
  }
}

最好的解決方案是 fork react-scripts ,這實際上在官方文檔中有提到,請參閱: Alternatives to Ejecting

如果您需要多次修改,例如使用 ant design 時,您可以像這樣組合多個功能:

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

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

您可以嘗試使用 simlinks,但反過來。

React 不會遵循 simlinks,但您可以將某些內容移動到源目錄,並創建一個指向它的 simlink。

在我的項目的根目錄中,我有一個節點服務器目錄,其中包含多個架構文件。 我想在前端使用它們,所以我:

  • 移動文件 /src
  • 在終端中,我進入了模式文件在服務器中的位置
  • ln -s SRC_PATH_OF_SCHEMA_FILE

這給了反應它正在尋找的東西,節點非常高興通過 simlinks 包含文件。

在我的項目中遇到了同樣的問題,並在官方的 create-react-app 文檔中找到了這個: https : //create-react-app.dev/docs/using-the-public-folder/

有一個“逃生艙口”可以在模塊系統之外添加資產:

如果將文件放入 public 文件夾,則 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 文件,你可能會遇到一個錯誤OUTSIDE OF SOURCE DIRECTORY

或者,您可以在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 文件夾以及其中 public 和 src 文件夾中的幾個其他文件。 create-react-app 命令有一個限制,即我們不能從 src 外部導入任何東西。

我的問題:

  1. 我必須導入在 src 文件夾外的 node_modules 文件夾中創建的 react-bootstrap css 文件。
  2. 我使用了 import "../node_modules/bootstrap/dist/css/bootstrap.min.css"; 但我在終端上遇到了錯誤。
  3. 我發現我可以創建一個新的 React 應用程序並按照從 A 到 G 的解決方案步驟來解決這個問題。

解決方案:A)創建一個新的反應應用程序,使用 create-react-app new

B) 新光盤

C)運行這個命令:“npm install react-bootstrap bootstrap@4.6.0”(沒有“”雙引號)

D) 在你的反應文件中把它導入 bootstrap: D.1) import "../node_modules/bootstrap/dist/css/bootstrap.min.css"; 或 D.2) 從“react-bootstrap/Button”導入按鈕;

E) 為 D.1) < button className="btn btn-success"> Bootstrap </button> 或 D.2) < Button variant="primary" 創建一個引導程序元素,例如 Button 或您的反應文件中的任何內容> 引導程序</Button>

F) 在終端:cd src

G)在終端:npm start,

這次就編譯成功了。

推理:一旦我按順序執行步驟 A 到 G,我就可以看到 react-bootstrap 終於工作了,這次我沒有收到任何錯誤。 (我想到了這個解決方案,因為:

  1. 我使用了 npm install "@material-ui/icons" 並將其安裝在 src 之外的 node_modules 文件夾中。
  2. 在我的反應文件中,我使用了 import Add from "@material-ui/icons/Add"
  3. 和 Material-ui 圖標對我來說很好用,但在這里我們也從外部 src 導入,從 node_modules .. 一切正常。 為什么這次從外部src導入沒有錯誤)
  4. 這就是為什么我剛剛創建了一個新的 React 應用程序,並遵循解決方案步驟 A 到 G。

這可以直接完成,而無需使用公用文件夾的路徑。 你可以這樣做

<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 在標記為回復答案中作為評論寫的內容,這實際上可以挽救生命:

“這是如何被接受的答案?只需在 .env 文件中設置 NODE_PATH=./src/.. 就可以輕松消除這種虛假限制。通過這樣做,您可以從 src 文件夾外部導入,而無需經歷相關的痛苦彈出你的應用程序。”

  • 火焰

編輯根據@cigien 的要求添加了更多信息。

上面的所有答案都很好地描述了為什么當我們使用 create-react-app 創建我們的 React 應用程序時我們不能使用來自公共文件夾的圖像。 我自己遇到了這個問題並閱讀了所有這些答案,我意識到,答案是“破解”應用程序以刪除限制我們的模塊。 有些答案甚至沒有撤消選項。 對於可以的“培訓”應用程序。

就我個人而言,我不想在我自己的項目中添加一個改變應用程序概念的解決方案,特別是在商業項目中。 @Flaom 解決方案是最簡單的,如果將來有任何變化,可以用另一種解決方案替換。 它沒有風險,可以隨時刪除,而且是最簡單的。

我可以通過使用file:作為本地依賴項“復制”外部文件來導入src/之外的文件。

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

然后

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

不需要ejectreact-app-rewired或其他 3rd 方解決方案。

這是我的代碼:

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;

它奏效了! 我越來越擅長修復錯誤哈哈。

如果您的文件位於公共文件夾中,並且如果您想在不彈出或不使用 react-app-rewired 的情況下導入它,那么在這種情況下,您可以通過域名和文件路徑並使用 axios 訪問文件。 示例:在 public 文件夾中有一個名為 favico.ico 的字體文件。 您想將其導入位於 src.xml 的文件中。 您可以使用以下邏輯訪問字體。

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”。 因此,使用此模式,您可以訪問位於公共文件夾中的資產。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM