Вы не можете реэкспортировать свой импорт так, как вы можете на других языках с поддержкой импорта / экспорта.
Вот что я имею в виду:
// Meta/index.jsx
import Title from './Title';
export Title;
Кажется разумным, верно? Все же это выдает синтаксическую ошибку.
Конечно, это может быть ошибка в Babel.js, но я не думаю, что это так. Если я правильно читаю Спецификацию ECMAScript2015 , то вы никогда не должны были этого делать. Что еще хуже.
После часа выяснения, почему Babel не будет компилировать мой код, я потратил еще час или два, чтобы понять, почему я не должен был ожидать, что мой код будет компилироваться в первую очередь. Давайте взглянем.
Вавилон выдает синтаксическую ошибку следующим образом:
Version: webpack 1.11.0
Time: 662ms
[0] multi main 40 bytes {0} [built] [1 error]
+ 7 hidden modules
ERROR in ./src/components/H1BGraph/Meta/index.jsx
Module build failed: SyntaxError: /Users/Swizec/Documents/random-coding/react-d3js-experiment/src/components/H1BGraph/Meta/index.jsx: Unexpected token (7:18)
5 | import Title from './Title';
6 |
> 7 | export Title;
| ^
8 |
Это не полезная ошибка. Это не только разумно, но и на других языках с поддержкой собственных модулей. Например, когда вы организуете файлы в каталог.
В этом случае есть a Title
и a Description
. Оба компонента наследуются от названного базового компонента BaseMeta
, но используются отдельно. Имеет смысл поместить эти три файла и несколько помощников в каталог с именем Meta
.
Принуждение программистов писать import Title from './Meta/Title'
нарушает абстракцию. Вам не нужно знать, как Meta
организованы внутренние компоненты и классы . Запись import Title from './Meta’
должна быть возможной.
Правильно?
Конечно, можно утверждать, что оба из этих компонентов подпадают под общий Meta
должен использоваться через родительский <Meta>
компонент. Тогда вы могли бы использовать Meta
и не беспокоиться об импорте либо Title
или Description
. Во многом это правда.
Но Meta
компонент должен быть в состоянии выставить любой из его внутренних компонентов для внешнего использования. Если JavaScript или Babel не согласны с архитектурой, им следует жаловаться на архитектуру и не выдавать синтаксическую ошибку.
Чтобы это работало, нам нужен такой грязный хак:
// Meta/index.jsx
import {Title as T} from './Title';
export class Title extends T {};
Гадкий, правда?
Мы используем псевдоним импорта ./Title
только для того, чтобы развернуть и экспортировать новый Title
компонент, который расширяется T
, но ничего не добавляет.
Мы должны сделать это, потому что импорт ES6 является константой — создание нового класса с тем же именем, которое уже существует, приведет к ошибке. Повторный экспорт Title
с другим именем будет распространять псевдонимы импорта через нашу кодовую базу как вирус.
Хотелось бы, чтобы эта глупость не была нужна, но это так.
Возможно, ответ заключается в том, к чему этот код компилируется. Возможно, где-то в результирующем коде ES5 есть более глубокая причина, почему JavaScript не может реэкспортировать, как все остальные.
Когда Babel готов, наш импорт → экспорт выглядит так:
function(module, exports, __webpack_require__) {
// Ugly but works :D
'use strict';
Object.defineProperty(exports, '__esModule', {
value: true
});
var _get = function get(_x, _x2, _x3) { var _again = true; _function: while (_again) { var object = _x, property = _x2, receiver = _x3; desc = parent = getter = undefined; _again = false; if (object === null) object = Function.prototype; var desc = Object.getOwnPropertyDescriptor(object, property); if (desc === undefined) { var parent = Object.getPrototypeOf(object); if (parent === null) { return undefined; } else { _x = parent; _x2 = property; _x3 = receiver; _again = true; continue _function; } } else if ('value' in desc) { return desc.value; } else { var getter = desc.get; if (getter === undefined) { return undefined; } return getter.call(receiver); } } };
function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError('Cannot call a class as a function'); } }
function _inherits(subClass, superClass) { if (typeof superClass !== 'function' && superClass !== null) { throw new TypeError('Super expression must either be null or a function, not ' + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; }
var _Title = __webpack_require__(8);
var Title = (function (_T) {
_inherits(Title, _T);
function Title() {
_classCallCheck(this, Title);
_get(Object.getPrototypeOf(Title.prototype), 'constructor', this).apply(this, arguments);
}
return Title;
})(_Title.Title);
exports.Title = Title;
;
/***/ }
Блин, много кода! Помните, как вы писали все это каждый раз, когда вам нужно наследование классов? Я тоже, винт наследования классов, если это то, что нужно. Неудивительно, что все так взволнованы модулями ES6.
Давайте посмотрим на ключевую часть: где Title
класс определяется как расширение без особенностей T
. Эта часть:
var _Title = __webpack_require__(8);
var Title = (function (_T) {
_inherits(Title, _T);
function Title() {
_classCallCheck(this, Title);
_get(Object.getPrototypeOf(Title.prototype), 'constructor', this).apply(this, arguments);
}
return Title;
})(_Title.Title);
exports.Title = Title;
;
Хорошо, это довольно знакомо. Если бы человек написал первую строку, это было бы что-то вроде var Foo = require('Foo’)
. require()
дает класс, и мы присваиваем его переменной.
Затем мы создаем замыкание, которое оборачивает область вокруг функции. Думайте об этой функции как о фабрике классов — она берет класс и создает новый расширенный класс.
Для создания этих новых классов замыкание создает функцию с именем Title
. Эта функция действует как конструктор объекта, так же, как функции обычно делают в JavaScript. Он использует _get
для выполнения какой-то магии, которая за мной. Я думаю, что она сама вызывает функцию, а также вызывает конструктор суперкласса, но я не уверен.
Просто чтобы сохранить интерес, Babel использует эту возможность, чтобы воспользоваться преимуществами поведения JavaScript, когда функции, которые не назначены переменной, всегда являются глобальными в текущей области видимости. Этот код вызывает _inherits
на Title
функцию, но , прежде чем он создан. Судя по всему _inherits
, он может заменить прототип подкласса экземпляром объекта суперкласса.
Это странно, но это работает. Слава богам с открытым исходным кодом, что Вавилон пишет эту магию для нас.
class X extends Y
не только легче написать, чем этот суп из JavaScript, но и легче (читай: возможно) понять.
Теперь мы знаем, что export class X extends Y {}
происходит, но мы до сих пор не ответили на главный вопрос: почему мы не можем просто реэкспортировать?
Я не думаю, что это потому, что JavaScript не может обработать что-то вроде этого:
var Title = (function (_T) {
// ...
})(__webpack_require__(8));
Хотя это не самый чистый код, он * должен * работать. Но взгляните на последнюю строку в предыдущем примере, строку с надписью exports.Title = Title
.
Если мы не сообщим коду, что экспортировать, как он узнает? Не будет Без имени скомпилированный код будет выглядеть так:
exports = __webpack_require__(8);
При этом мы можем реэкспортировать только код одного модуля. Который работает кстати, вы всегда можете написать export default X
.
Если вы хотите реэкспортировать несколько модулей, вам придется придерживаться этого уродливого обходного пути. Я думаю, что это глупо, но это лучшее, что может сделать JavaScript, пока не получит отражение, и мы наконец-то можем ответить на вопрос «Эй, как программист назвал эту переменную?»
Я думаю, что это будет в ES7. Посмотрим.
PS: официальный способ реэкспорта модулей в ES6 выглядит так:
// to export a member
export { encrypt as en } from 'lib/crypto';
// or to export everything
export * from 'lib/crypto';
Но это все еще выглядит странно, не подходит для экспорта по умолчанию и не решает проблему «сохранить имя».
Благодарю Юре Чухалева, Анже Печара, Уилла Фангуя, Колетту Теске и Дэвида Байрда за чтение черновых версий этой статьи
связанные с
Вы должны следовать за мной в твиттере, здесь .