Динамический импорт в React не работает при попытке импортировать компонент в другой каталог

Привет всем, я пробовал динамический импорт в ответ на рендеринг моих компонентов для приложения, созданного с помощью CRA (create-response-app), и хотя он отлично работает в некоторых случаях, но в некоторых он возвращает ошибку не может загрузить модуль, например, я загрузил компонент (помещенный в каталог под src) динамически в моем index.js, который отлично работает, но когда я пытаюсь визуализировать дочерний или вложенный компонент внутри него, также с динамическим подходом импорта, он дает ошибку, не могу загрузить модуль. Обратите внимание, что эта ошибка возникает только в том случае, если вложенный компонент размещен вне каталога исходного родительского компонента, достаточно разговора, вот код.

Мой index.js помещен в src.

    import React, { Component } from 'react';
    import ReactDOM from 'react-dom';
    import './index.css';

    class Dynamic extends Component {
      constructor(props) {
        super(props);
        this.state = { module: null };
      }
      componentDidMount() {
          console.log('in comp mount')
          //alert("in comp mount")
        const { path } = this.props;
        import(`${path}`)
          .then(module => this.setState({ module: module.default }))
     }
      render() {
          console.log('in render')
         // alert("in render")
        const { module: Component } = this.state; // Assigning to new variable names @see: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Destructuring_assignment
        return(
          <div>
            {Component && <Component path= '../FooterComp/Footer' />}
          </div>
        )
      }
    }

ReactDOM.render(<Dynamic path='./Components/FirstComponent' />, document.getElementById('root'));

FirstComponent.js помещен в каталог Components в src.

import React, { Component } from 'react';
import logo from '../logo.svg';
import '../FirstApp.css';

class App extends Component {

    constructor(props) {
    super(props);
    this.state = { module: null };
  }
  componentDidMount() {
      console.log('in comp mount')
      //alert("in comp mount")
    const { path } = this.props;
    alert(path)
    import(`${path}`)
      .then(module => this.setState({ module: module.default }))
 }

  render() {
      const { module: Component } = this.state;
    return (
      <div className="App">
        <header className="App-header">
        <img src={logo} className="App-logo" alt="logo" />
          <p>
            Edit <code>src/App.js</code> and save to reload.
          </p>
          <a
            className="App-link"
            href="https://reactjs.org"
            target="_blank"
            rel="noopener noreferrer"
          >
            Learn React
          </a>
        </header>
        {Component && <Component />}
      </div>
    );
  }
}

export default App;

Footer.js помещен в каталог FooterComp в src.

import React, { Component } from 'react';
import '../App.css';

class Footer extends Component {
    componentDidMount()
    {
        console.log('in componentDidMount of Footer')
    }
  render() {
      console.log('in render of Footer')
    return (
      <div className="App">
        <h1>Edited by Me</h1>
      </div>
    );
  }
}

export default Footer;

Почему это работает, когда я ссылаюсь на свой первый компонент из моего index.js, но не работает для компонента нижнего колонтитула при попытке импорта в свой первый компонент?

Сообщение об ошибке: Error: Cannot find module '../FooterComp/Footer'

Также обратите внимание, что если я помещаю компонент Footer в тот же каталог, что и Firstcomponent, и настраиваю путь, он отлично работает.


person JayD    schedule 18.01.2019    source источник


Ответы (2)


Есть ограничение при использовании динамического импорта с переменными частями.

Документы Webpack

Невозможно использовать полностью динамический оператор импорта, такой как import (foo). Потому что foo потенциально может быть любым путем к любому файлу в вашей системе или проекте.

Import () должен содержать хотя бы некоторую информацию о том, где расположен модуль. Объединение может быть ограничено определенным каталогом или набором файлов, поэтому при использовании динамического выражения включается каждый модуль, который потенциально может быть запрошен при вызове import (). Например, import (./locale/${language}.json) вызовет каждый .json файл в каталоге ./locale для включения в новый чанк. Во время выполнения, когда переменный язык вычислен, любой файл, например english.json или german.json, будет доступен для использования.

В вашем случае во время сборки для динамического импорта в компоненте FirstComponent сборка была ограничена каталогом, в котором находился компонент FirstComponent, то есть каталогом Components.

Это означает, что webpack найдет все файлы в каталоге Components, а затем создаст чанки. для них. Затем во время выполнения, когда вызывается динамический импорт, webpack будет обслуживать фрагмент, соответствующий переданному значению.

Поскольку вы передали path= '../FooterComp/Footer', у него нет соответствующего фрагмента, поэтому webpack выдаст ошибку.

То же самое и для компонента Dynamic. Если вы попытаетесь динамически импортировать с переменными частями для файлов, находящихся за пределами папки src, вы получите ту же ошибку.

Чтобы решить эту проблему, у вас есть несколько вариантов

  1. поместите оба файла в одну папку

i.e

'src/Components/FirstComponent.js'

'src/Components/Footer.js'

И использовать

// In FirstComponent.js
   componentDidMount() {
      const { path } = this.props;
      import(`${path}`)
      .then(module => this.setState({ module: module.default }))   
   }


{Component && <Component path='./Footer' />} // Index.js
  1. быть более конкретным, насколько это возможно

i.e

// In FirstComponent.js
 componentDidMount() {
      const { path } = this.props;
      import(`../FooterComp/${path}`)
      .then(module => this.setState({ module: module.default }))   
 }

И использовать

{Component && <Component path='Footer' />} //  In index.js
person anonymous    schedule 18.01.2019
comment
Спасибо, второй подход работает нормально, но не могли бы вы предоставить несколько ссылок или документации, которые относятся к ограничению, которое вы указали, чтобы я мог более четко об этом подумать и не забыл проголосовать за вопрос :) - person JayD; 19.01.2019

Если FooterComp находится под src, путь должен быть './FooterComp/Footer', а не '../FooterComp/Footer'


Изменить

Index.js

    render() {
          console.log('in render')
         // alert("in render")
        const { module: Component } = this.state; 
        return(
          <div>
            {Component && <Component path='./Components/FirstComponent' />}
          </div>
        )
      }
    }

ReactDOM.render(<Dynamic />, document.getElementById('root'));

FirstComponent.js

render() {
      const { module: Component } = this.state;
    return (
      <div className="App">
        <header className="App-header">
        <img src={logo} className="App-logo" alt="logo" />
          <p>
            Edit <code>src/App.js</code> and save to reload.
          </p>
          <a
            className="App-link"
            href="https://reactjs.org"
            target="_blank"
            rel="noopener noreferrer"
          >
            Learn React
          </a>
        </header>
        {Component && <Component path= '../FooterComp/Footer' />}
      </div>
    );
  }
person Derf Mongrel    schedule 18.01.2019
comment
нет, вы видите, что FirstComponent находится в каталоге Components в src, и из этого я импортирую компонент нижнего колонтитула, который помещается в каталог FooterComp, поэтому я думаю, мне нужно указать путь ../FooterComp/Footer. - person JayD; 18.01.2019
comment
Ошибка возникает из-за того, что он проверяет местоположение файла из файла index.js. - person Derf Mongrel; 18.01.2019
comment
нет, он передает путь в качестве реквизита к первому компоненту, и модуль будет импортирован при рендеринге FirstComponent и, следовательно, путь для ссылки на нижний колонтитул от firstComponent i тот, который я предоставил - person JayD; 18.01.2019
comment
Попробуйте то, что я редактировал выше. Я изменил оба пути на index.js и добавил путь на FirstComponent.js. - person Derf Mongrel; 18.01.2019