Разделение компонентов 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. Но главная проблема заключается в том, что веб-пакет не может выполнять интеллектуальную оптимизацию, поскольку он не знает, какие функции вы не используете. Так что лучше всего избегать всего этого.