create-react-app นำเข้าข้อ จำกัด นอกไดเรกทอรี src


150

ฉันใช้ create-react-app ฉันพยายามโทรภาพจากโฟลเดอร์สาธารณะของฉันจากไฟล์ในsrc/componentsตัว ฉันได้รับข้อความแสดงข้อผิดพลาดนี้

./src/components/website_index.js ไม่พบโมดูล: คุณพยายามที่จะนำเข้า ../../public/images/logo/WC-BlackonWhite.jpg ซึ่งอยู่นอกไดเรกทอรีโครงการ src / ไม่รองรับการนำเข้าญาตินอก src / คุณสามารถย้ายภายใน src / หรือเพิ่ม symlink ลงใน node_modules / ของโครงการ

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

ฉันได้อ่านหลายสิ่งหลายอย่างที่บอกว่าคุณสามารถนำเข้าเส้นทาง แต่นั่นก็ยังไม่ทำงานสำหรับฉัน ความช่วยเหลือใด ๆ ที่จะได้รับการชื่นชมอย่างมาก. ฉันรู้ว่ามีคำถามมากมายเช่นนี้ แต่พวกเขากำลังบอกให้ฉันนำเข้าโลโก้หรือรูปภาพดังนั้นฉันจึงพลาดอะไรบางอย่างในภาพรวม


2
คุณต้องการ../public/images/logo_2016.pngคุณเพิ่มขึ้นสองครั้งก่อนจากโฟลเดอร์คอมโพเนนต์จากนั้นออกจากโฟลเดอร์ src
Chris G

./src/components/website_index.js ไม่พบโมดูล: คุณพยายามที่จะนำเข้า ../../public/images/logo/WC-BlackonWhite.jpg ซึ่งอยู่นอกไดเรกทอรีโครงการ src / ไม่รองรับการนำเข้าญาตินอก src / คุณสามารถย้ายภายใน src / หรือเพิ่ม symlink ลงใน node_modules / ของโครงการ
David Brierton

ความคิดเห็นของฉันถือว่าpublicโฟลเดอร์ของคุณอยู่ในsrcโฟลเดอร์ของคุณโดยตรง ความคิดเห็นที่ไม่แสดงความคิดเห็นของคุณจะแสดงเส้นทางเก่าที่เริ่มต้นด้วย../..ดังนั้นไม่แน่ใจว่าประเด็นของคุณคืออะไร?
Chris G

3
ไม่มีสาธารณะอยู่ในระดับเดียวกับ src
David Brierton

หมายความว่า "หรือเพิ่ม symlink จาก node_modules /" ของโครงการ?
Julha

คำตอบ:


127

นี่เป็นข้อ จำกัด พิเศษที่เพิ่มโดยผู้พัฒนา create-react-app มันถูกนำมาใช้ในเพื่อให้แน่ใจว่าไฟล์ที่อาศัยอยู่ในModuleScopePlugin src/ปลั๊กอินนั้นรับรองว่าการนำเข้าสัมพัทธ์จากไดเรกทอรีต้นทางของแอพไม่สามารถเข้าถึงจากภายนอกได้

คุณสามารถปิดใช้งานฟีเจอร์นี้ได้ แต่หลังจากejectการทำงานของโครงการ create-react-app

ฟีเจอร์และการอัพเดทส่วนใหญ่นั้นซ่อนอยู่ภายในระบบการสร้างแอปแบบตอบโต้ หากคุณทำให้ejectคุณไม่มีฟีเจอร์และการอัปเดตอีกต่อไป ดังนั้นหากคุณยังไม่พร้อมที่จะจัดการและกำหนดค่าแอพพลิเคชั่นที่รวมไว้เพื่อกำหนดค่า webpack และอื่น ๆ - อย่าejectดำเนินการ

เล่นตามกฎที่มีอยู่ (เลื่อนไปที่ src) แต่ตอนนี้คุณสามารถทราบวิธีการลบข้อ จำกัด : ทำejectและลบModuleScopePluginจากการตั้งค่าไฟล์ webpack


ตั้งแต่สร้างปฏิกิริยาแอป v0.4.0NODE_PATHตัวแปรสภาพแวดล้อมจะช่วยให้การระบุเส้นทางสำหรับการนำเข้าแน่นอน และตั้งแต่v3.0.0 NODE_PATHจะเลิกในความโปรดปรานของการตั้งค่าbaseUrlในหรือjsconfig.jsontsconfig.json

การนำเข้าแบบสัมบูรณ์อนุญาตให้ใช้import App from 'App'แทนimport App from './App'สัมพันธ์กับค่าที่ระบุใน URL พื้นฐาน

คุณสมบัตินี้มีประโยชน์อย่างยิ่งสำหรับการ monorepos หรือคำถามการกำหนดค่าอื่น ๆ แต่ไม่ใช่สำหรับการนำเข้ารูปภาพหรือสิ่งอื่นจากpublicโฟลเดอร์

เนื้อหาของpublicโฟลเดอร์จะถูกวางไว้ในbuildโฟลเดอร์และให้บริการโดย url ที่เกี่ยวข้อง นอกจากนี้ทุกอย่างที่นำเข้าจะถูกประมวลผลโดย webpack และจะถูกวางไว้ในbuildโฟลเดอร์

หากคุณนำเข้าบางสิ่งจากpublicโฟลเดอร์อาจเป็นสิ่งที่ซ้ำกันในbuildโฟลเดอร์และจะมี URL ที่แตกต่างกันสองแบบ (หรือด้วยวิธีการโหลดที่แตกต่างกัน) ซึ่งท้ายที่สุดแล้วขนาดแพคเกจการดาวน์โหลดจะแย่ลง

การนำเข้าจากโฟลเดอร์srcเป็นที่ต้องการและมีข้อดี ทุกอย่างจะได้รับการบรรจุโดย webpack ไปมัดกับชิ้นขนาดที่เหมาะสมและมีประสิทธิภาพในการโหลดที่ดีที่สุด


มีโซลูชั่นระดับกลางคือระบบrewireซึ่งช่วยให้คุณสามารถปรับเปลี่ยนการตั้งค่า webpack โดยทางโปรแกรม แต่เอาModuleScopePluginปลั๊กอินไม่ได้เป็นดีแก้ปัญหา; srcมันจะดีกว่าที่จะเพิ่มการทำงานอย่างเต็มที่ไดเรกทอรีเพิ่มเติมที่คล้ายกับ

ขณะนี้create-react-appไม่รองรับไดเรกทอรีเพิ่มเติมนอกเหนือจากsrcในโฟลเดอร์ราก ซึ่งสามารถทำได้โดยใช้react-app-rewire-alias


3
ถ้าคุณสร้าง symlink ภายใน. / src และนำเข้าจากที่นั่น - บิลด์ไม่ทำงาน (ปลั๊กอิน babel ไม่แปลงแหล่งที่มาในโฟลเดอร์ symlinked) ดังนั้นด้วยข้อ จำกัด นี้เปิดและไม่เชื่อมโยงภายใต้ src คุณจะถูกวางไว้ในคุก 'ไม่มีการแบ่งปันรหัสกับโครงการอื่น ๆ ' อย่างมีประสิทธิภาพ (เว้นแต่คุณจะเลือกย้าย / นำออกจาก CRA อย่างสมบูรณ์)
VP

2
@VP แต่ข้อผิดพลาดแจ้งว่า "เพิ่ม symlink ลงใน node_modules /." ของโครงการนี่มันใช้งานไม่ได้เหรอ?
adrianmc

พวกเขาควรตั้งค่าสถานะเพื่อปิดการใช้งานนี้เมื่อทำงานcreate-react-appสำหรับผู้ที่ไม่ต้องการเลื่อนการตั้งค่า webpack โดยส่วนตัวแล้วฉันขับดีดออกมา แต่บางคนไม่สบายใจ
TetraDev

2
@adrianmc การเพิ่ม symlink จาก node_modules ของโปรเจ็กต์ไม่ทำงานเนื่องจากโปรเจ็กต์จำนวนมากใช้การแบ่งใช้คอมโพเนนต์ / รหัสที่ไม่ใช่ node_modules ตัวอย่างเช่นฉันแชร์รหัสระหว่าง React Native และ React native web 'ตัวอย่าง' เหล่านั้นไม่ใช่ node_modules และจริง ๆ แล้ว 2 โครงการเหล่านี้มี node_modules ที่แตกต่างกัน
รองประธาน

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

สิ่งนี้จะลบ ModuleScopePlugin ออกจากปลั๊กอิน WebPack ที่ใช้แล้ว แต่ให้เหลือเหมือนเดิมและลบความจำเป็นที่จะนำออก


5
คำตอบที่ดี คุณสามารถใช้ประโยชน์ต่อไปreact-app-rewiredด้วย [ github.com/arackaf/customize-cra](customize-cra) จากนั้นตั้งค่าจะใช้เพียงและbabelInclude([path.resolve('src'), path.resolve('../common')]) removeModuleScopePlugin()
ivosh

คุณช่วยยืนยันได้ไหมว่าฉันควรวางข้อมูลโค้ดนี้ไว้ที่ไหน
bijayshrestha

1
@bijayshrestha อ่านไฟล์ readme ของโครงการ react-app-rewired ที่นั่นมันอธิบายวิธีการตั้งค่า ในระหว่างการตั้งค่าคุณจะสร้างconfig-overrides.jsไฟล์ที่คุณสามารถวางรหัสได้
Lukas Bach

การลบModuleScopePluginปลั๊กอินไม่ใช่ความคิดที่ดี จะเป็นการดีกว่าถ้าคุณเพิ่มไดเรกทอรีเพิ่มเติมที่ทำงานคล้ายกับการsrcใช้react-app-rewire-alias
oklas

1
โซลูชันนี้ใช้กับ typescript ได้หรือไม่ ฉันไม่สามารถทำงานกับ TS ได้
user3053247

27

เพื่อเสนอข้อมูลเพิ่มเติมเล็กน้อยสำหรับคำตอบของผู้อื่น คุณมีสองตัวเลือกเกี่ยวกับวิธีส่งไฟล์. png ไปยังผู้ใช้ โครงสร้างไฟล์ควรสอดคล้องกับวิธีที่คุณเลือก สองตัวเลือกคือ:

  1. ใช้ระบบโมดูล ( import x from y) ที่ให้มาพร้อมกับ react-create-app และรวมเข้ากับ JS ของคุณ วางภาพภายในsrcโฟลเดอร์

  2. บริการจากpublicโฟลเดอร์และให้ Node ให้บริการไฟล์ สร้างปฏิกิริยา-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 / หรือเพิ่ม symlink ลงใน 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 / directory ไม่รองรับการนำเข้าญาตินอก src / '
Arkady

คำตอบของคุณคือ IMO ที่ถูกต้อง แต่ฉันใช้เสรีภาพในการชี้แจงว่าคุณกำลังพูดถึงเส้นทางใน<img src="...">ไม่ใช่การนำเข้า
Dmitry

1
นี่คือสิ่งที่ฉันกำลังมองหา! ฉันเพิ่งเพิ่มโฟลเดอร์ "images" ไปยังไดเรกทอรี "src" ของ CRA ของฉันแล้วฉันก็สามารถใช้งานได้<img src="/images/logo.png" alt="Logo" />
Amos Long

12

คุณต้องย้ายไปWC-BlackonWhite.jpgยังsrcไดเรกทอรีของคุณ publicไดเรกทอรีสำหรับไฟล์คงที่จะได้รับการเชื่อมโยงโดยตรงใน HTML (เช่น favicon) ที่ไม่ว่าสิ่งที่คุณกำลังจะนำเข้าโดยตรงในกำของคุณ


ฉันได้เห็นคนอื่นทำอย่างนี้ นั่นเป็นเหตุผลที่ฉันคิดว่านี่เป็นวิธีที่เหมาะสม
David Brierton

@DavidBrierton: ข้อควรระวังนี้อาจถูกเพิ่มเข้าไปใน create-react-app เวอร์ชันที่ใหม่กว่า
Joe Clay

8

มีคำตอบสองสามข้อที่ให้บริการโซลูชั่น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",
    ...
},

การตั้งค่า-overrides.js

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

module.exports = removeModuleScopePlugin()

1
คุณบันทึกวันของฉัน!
lmiguelvargasf

6

ลบออกโดยใช้ 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! 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"
},

การตั้งค่า-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ด้วยไลบรารีตัวแก้ไข

สิ่งนี้จะได้ผล:

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 แทน:

การตั้งค่า-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 ใช่ไหม
Beau Smith


1

หากคุณต้องการการดัดแปลงหลายอย่างเช่นเมื่อใช้การออกแบบมดคุณสามารถรวมฟังก์ชั่นหลายอย่างเช่นนี้:

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

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.