Разделение компонентов React на отдельные файлы без явного импорта

Я собираюсь написать приложение React на основе Ruby Sinatra. Файл main.js отображает приложение:

import React from 'react';
import ReactDOM from 'react-dom';
import Galery from './components/Galery'

ReactDOM.render(
  <Galery />,
  document.getElementById('app')
);

Раньше все мои компоненты были в одном файле, но я хочу разделить их на отдельные файлы. Мне удалось выполнить этот прогон, только если я импортирую дочерние компоненты в файл компонентов каждого родителя, например, в Galery.js:

import React, { Component } from 'react';
import Image from 'Image'

class Galery extends Component {
  ...
    <Image ... />
  ...
}

Можно ли избежать импорта необходимых компонентов в явном виде и вместо этого загрузить их в файл main.js? Также было бы хорошо не импортировать модуль Component в каждый файл.

Вот мой конфиг веб-пакета:

module.exports = {
  entry: './react/main.js',
  output: {
    path: __dirname,
    filename: './public/bundle.js'
  },
  resolve: {
    root: __dirname,
    alias: {

    },
    extensions: ['', '.js', '.jsx']
  },
  module: {
    loaders: [
      {
        loader: 'babel-loader',
        query: {
          presets: ['react', 'es2015']
        },
        test: /\.js$/,
        exclude: /(node_modules|bower_components)/
      }
    ]
  }
};

1 ответ

Решение

Короткий ответ:

Настоятельно рекомендуется использовать явный импорт везде, где вам нужно, потому что такие инструменты, как webpack делать интеллектуальные оптимизации по размеру пакета, удаляя функции, которые не используются. Учитывая это, самый короткий ответ: как вы используете webpack + babel + React, невозможно избежать определения imports за файл.


Более длинный ответ:

Да, вы можете сделать это, но это не так просто. В отличие от Ruby, поиск констант / переменных не работает в JavaScript. В Ruby прекрасно работает следующее:

# a.rb
A = 10

# b.rb
require "./a.rb"
puts a # => 10

Это потому, что когда файл a.rb анализируется и включается в b.rbв Ruby нет дополнительного подпространства имен. Все единицы верхнего уровня существуют так, как если бы они были определены внутри b.rb, Подробнее об этом

Чтобы сравнить это с JS, мне нужно немного разобраться в том, как работает включение модуля. Это довольно сложная ситуация на данный момент. Давайте сначала рассмотрим NodeJS. В этой не браузерной среде нет import функциональность еще реализована, за исключением самых передовых версий с дополнительными флагами (v 9 на сегодняшний день). Поэтому, когда вы используете что-то вроде webpack а также importвнутренне происходит то, что они конвертируются в собственный веб-пакет require прокладка. И путь import а также require преобразование get немного отличается, потому что первый является загрузчиком статического стиля, тогда как последний является динамическим загрузчиком стиля. На самом базовом уровне это означает import заявления должны быть в верхней части файла, и require операторы могут быть где угодно, и разрешение файла происходит, когда интерпретатор встречает эту строку. Это имеет странные эффекты, как вы увидите ниже.

Путь NodeJS require работает путем определения module.exports объект из включенного файла. Этот объект определяет, какие функции / объекты выставлены снаружи. Следовательно, в отличие от Ruby, уже существует неявное локальное пространство имен (или, если хотите, группировка) module.exportsвместо глобального $LOADED_FEATURES один:

// a.js
const a = 10;
module.exports = { a: a };

// b.js

const a = require('./a.js');
console.log(a); // { a: 10 };

Один из способов обойти это - глобальные переменные. Как и в Ruby, JavaScript имеет неявное глобальное пространство имен, особенно чаще встречающееся в браузерах через window, а также global в NodeJS. Одна идея заключается в следующем:

// main.js

import React from 'react';
import ReactDOM from 'react-dom';

import Image from 'components/image.js';
import Gallery from 'components/gallery.js';

window.React = React;
window.ReactDOM = ReactDOM;
window.Image = Image;
window.Gallery = Gallery;

// gallery.js

export default class Gallery extends React.Component {
  render() {
    return <Image />;
  }
}

Тем не менее, это не работает. Для того, чтобы подражать фактической ES6 import функциональность - это статически определенный набор файлов и функций, веб-пакет пытается эмулировать их через собственный веб-пакет require функция. И это не делает глобально связанные константы доступными к моменту завершения импорта. Таким образом, чтобы сделать эту работу, один способ обойти это использовать старые require загрузка стиля, которая меняет собственный стиль веб-пакета require функция работает. Изменяя выше, чтобы:

// main.js

const React = require('react');
const ReactDOM = require('react-dom');

window.React = React;
window.ReactDOM = ReactDOM;

const Image = require('components/image.js').default;
const Gallery = require('components/gallery.js').default;

window.Image = Image;
window.Gallery = Gallery;

// gallery.js

export default class Gallery extends React.Component {
  render() {
    return <Image />;
  }
}

Как видите, это слишком много работы для запуска приложения JavaScript. Но главная проблема заключается в том, что веб-пакет не может выполнять интеллектуальную оптимизацию, поскольку он не знает, какие функции вы не используете. Так что лучше всего избегать всего этого.

Другие вопросы по тегам