React: Формализовать экспорт ES верхнего уровня

Созданный на 9 нояб. 2017  ·  104Комментарии  ·  Источник: facebook/react

В настоящее время мы отправляем только CommonJS-версии всех пакетов. Однако в будущем мы можем захотеть отправлять их как ESM (https://github.com/facebook/react/issues/10021).

Мы не можем легко это сделать, потому что мы еще не решили, как будет выглядеть экспорт ES верхнего уровня из каждого пакета. Например, есть ли у react набор именованных экспортов, а также экспорт по умолчанию с именем React ? Должны ли мы поощрять людей к import * для лучшего тряски дерева? Как насчет react-test-renderer/shallow который в настоящее время экспортирует класс (и, следовательно, начал бы сбой в Node, если бы он был преобразован в экспорт по умолчанию)?

Build Infrastructure React Core Team Breaking Change Discussion

Самый полезный комментарий

Сейчас почти 2020 год, я хотел бы знать, есть ли обновления от официальной команды FB? Будут ли связанные с этим изменения в React v17?

Все 104 Комментарий

Imho import * - отличный вариант, я тоже не против использования экспорта по умолчанию, но его не следует использовать для повторного экспорта других вещей, как в этом примере:

export const Component = ...
export default React
React.Component = Component

но его не следует использовать для реэкспорта других вещей, как в этом примере:

Есть техническая причина, почему? (Помимо двух способов сделать одно и то же.)

У меня сложилось впечатление, что у людей, которые будут импортировать * (и не использовать значение по умолчанию), не будет проблем с тряской дерева, поскольку значение по умолчанию останется неиспользованным. Но, возможно, я переоцениваю Rollup и т. Д.

На эти вопросы, вероятно, лучше всего ответит @lukastaegert. Не уверен, что что-то изменилось с https://github.com/facebook/react/issues/10021#issuecomment -335128611

Кроме того, Rollup - не единственный шейкер деревьев, и хотя алгоритм встряхивания деревьев в webpack хуже, чем тот, который используется в rollup, его использование, вероятно, намного выше, чем у rollup (оба инструмента отлично справляются с работой, я не хочу никого обидеть , просто констатируем факты), и если мы (как сообщество) можем помочь обоим инструментам одновременно, мы должны делать это при любой возможности.

будет ли tree-shaking делать что-нибудь в случае React, учитывая, что все предварительно обработано в единый плоский пакет? Интересно, каков основной стиль импорта для React, лично я склонен рассматривать его как экспорт по умолчанию, например, React.Component , React.Children но иногда делаю указанную вещь с помощью cloneElement

Как @gaearon уже заявлял в другом месте, ожидается, что улучшение размера в случае реакции будет минимальным. Тем не менее, преимущества ЕСТЬ:

  • React.Children, вероятно, могут быть удалены в некоторых случаях (так я слышал 😉)
  • Сам React может быть поднят в топ с помощью сборщиков модулей, которые это поддерживают. Это может снова удалить довольно много байтов, а также может дать очень небольшое улучшение производительности. Основное улучшение будет заключаться в том, что не обязательно должна быть другая переменная, которая ссылается на React.Component для каждого модуля, а должна быть только одна переменная, которая используется повсюду (именно так обычно это делает свертка). Кроме того, хотя это всего лишь предположение, это может снизить вероятность выхода из строя модуля ModuleConcatenationPlugin веб-пакета.
  • Статический анализ для реакции проще не только для сборщиков модулей, но также, например, для IDE и других инструментов. Многие такие инструменты уже неплохо справляются с этой задачей для модулей CJS, но, в конце концов, с их стороны приходится много гадать. С модулями ES6 анализ становится простой задачей.

Что касается вида экспорта, конечно, только именованный экспорт действительно обеспечивает преимущество легкого встряхивания дерева (если вы не используете GCC, который мог бы сделать немного больше в своем агрессивном движении и, возможно, последний накопительный пакет, если вам действительно повезет) . Сложнее решить вопрос, предоставляете ли вы также экспорт по умолчанию:

  • PRO: безболезненная миграция для существующих баз кода ES6 (например, того, что описывает @jquense )
  • ПРОТИВ: Так как все прикреплено к общему объекту, как только этот объект включен, все его ключи включаются сразу, что снова препятствует любым попыткам встряхивания дерева. Даже GCC может быть здесь нелегко.

В качестве стратегии миграции с двумя версиями вы можете добавить экспорт по умолчанию в следующей версии для целей совместимости, который объявлен устаревшим (он может даже отображать предупреждение через геттер и т. Д.), А затем удалить его в более поздней версии.

Это тоже интересный случай: https://github.com/facebook/react/issues/11526. Хотя monkeypatching для тестирования немного сомнительна, мы должны помнить о том, чтобы нарушить это (или найти обходной путь для этого).

Пришел сюда через этот разговор в Твиттере . Для меня есть четкий правильный ответ на этот вопрос: React и ReactDOM должны экспортировать только именованный экспорт. Они не являются объектами, которые содержат состояние или которые другие библиотеки могут изменять или присоединять к свойствам (несмотря на # 11526) - единственная причина, по которой они существуют, - это место для 'размещения' Component , createElement и так далее. Другими словами, пространства имен, которые следует импортировать как таковые.

(Это также облегчает жизнь сборщикам пакетов, но это ни здесь, ни там.)

Конечно, это серьезное изменение для людей, которые в настоящее время используют импорт и транспилирование по умолчанию. @lukastaegert, вероятно, имеет здесь правильную идею, используя аксессоры для вывода предупреждений об устаревании. Возможно, их можно было удалить в версии 17?

Но у меня нет готового предложения для # 11526. Возможно, при поставке ESM в любом случае пришлось бы дождаться версии 17 по этой причине, и в этом случае не нужно было бы беспокоиться о предупреждениях об устаревании.

Людям действительно нравится

import React, { Component } from 'react'

так что убедить их отказаться от этого может быть сложно.

Думаю, это не так уж и плохо, даже если немного странно:

import * as React from 'react';
import { Component } from 'react';

Чтобы уточнить, нам нужно, чтобы React находился в области видимости (в данном случае как пространство имен), потому что JSX переносится в React.createElement() . Мы могли бы взломать JSX и сказать, что вместо этого он зависит от глобальной функции jsx() . Тогда импорт будет выглядеть так:

import {jsx, Component} from 'react';

что, может быть, и нормально, но огромное изменение. Это также будет означать, что для сборок React UMD теперь также необходимо установить window.jsx .

Почему я предлагаю jsx вместо createElement ? Что ж, createElement уже перегружено ( document.createElement ), и хотя это нормально с квалификатором React. , без его утверждения на глобальном уровне это слишком много. Tbh Я не в восторге от любого из этих вариантов и думаю, что это, вероятно, будет лучшей золотой серединой:

import * as React from 'react';
import { Component } from 'react';

и по умолчанию JSX будет транслироваться в React.createElement .

Признание: мне всегда было немного странно, что вам нужно явно импортировать React , чтобы использовать JSX, даже если вы на самом деле нигде не используете этот идентификатор. Возможно, в будущем транспилеры смогут вставлять import * as React from 'react' (настраивается для Preact и т. Д.) При обнаружении JSX, если он еще не существует? Таким образом, вам нужно будет сделать только это ...

import { Component } from 'react';

... и об импорте пространства имен позаботятся автоматически.

Может быть, в далеком будущем. На данный момент нам нужно убедиться, что транспайлеры работают с другими модульными системами (CommonJS или глобальными). Сделать это настраиваемым также является препятствием и еще больше раскалывает сообщество.

То, что предложил @ Rich-Harris (вставка определенного импорта при использовании jsx), легко сделать с помощью плагина transpilers. Сообществу придется обновить свои babel-plugin-transform-react-jsx и все. И, конечно, даже существующие настройки будут работать, если только один из них добавит в файл import * as React from 'react'; .

Конечно, нам нужно рассмотреть другие модульные системы, но это не кажется сложной проблемой. Есть ли какие-то конкретные подводные камни?

Конечно, нам нужно рассмотреть другие модульные системы, но это не кажется сложной проблемой. Есть ли какие-то конкретные подводные камни?

Я не знаю, каковы ваши конкретные предложения относительно того, как с этим справиться? Что будет по умолчанию для плагина Babel JSX?

Людям действительно нравится

import React, { Component } from 'react'

Какие люди? Выходи, и я посмеюсь над тобой.

Я делал это много раз 🙂 Я уверен, что видел это и в других местах.

По умолчанию на данный момент React.createElement и он почти не изменится. Единственная проблема в том, что он предполагает глобальный сейчас (или уже доступен в области видимости).

Я думаю, что, поскольку модули es в основном являются стандартным способом (хотя еще не всеми) создания модулей, разумно предположить, что большинство использует (или должно) его использовать. Подавляющее большинство уже использует различные инструменты этапа сборки для создания своих пакетов - что еще более верно в этом обсуждении, потому что мы говорим о транспиляции синтаксиса jsx . Изменение поведения плагина jsx по умолчанию на автоматическую вставку React.createElement в область видимости - это разумная вещь, которую нужно сделать. Мы как раз в идеальное время для этого изменения, скоро выйдет babel @ 7 (-ish). С недавним добавлением babel-helper-module-imports стало проще, чем когда-либо, вставить в файл правильный тип импорта (es / cjs).

Возможность настройки для выхода из сегодняшнего поведения (при условии наличия в масштабе) действительно кажется незначительным изменением в конфигурации, необходимым для меньшинства пользователей, и улучшением (конечно, не большим, но все же) для большинства.

Следует ли поощрять импорт * для лучшего тряски деревьев?

Благодаря @alexlamsl uglify-es устранил штраф export default в распространенных сценариях:

$ cat mod.js 
export default {
    foo: 1,
    bar: 2,
    square: (x) => x * x,
    cube: (x) => x * x * x,
};
$ cat main.js 
import mod from './mod.js'
console.log(mod.foo, mod.cube(mod.bar));



md5-d6d4ede42fc8d7f66e23b62d7795acb9



$ uglifyjs -V
uglify-es 3.2.1

```js
$ cat bundle.js | uglifyjs --toplevel -bc
var mod_foo = 1, mod_bar = 2, mod_cube = x => x * x * x;

console.log(mod_foo, mod_cube(mod_bar));
$ cat bundle.js | uglifyjs --toplevel -mc passes=3
console.log(1,8);

вау, это отличная новость 👏 считается ли сейчас uglify-es стабильным? Я помню, как вы упомянули несколько месяцев назад, что его еще нет, но я помню это неправильно, поэтому не уверен.

В любом случае - это все и хорошо в мире свертки, но, учитывая, что React объединяется в основном в приложения, а те, в основном, используют webpack которые по умолчанию не поднимают область видимости, я бы все же сказал, что следует избегать экспорта объекта по умолчанию, чтобы помочь другим инструментам, кроме uglisy-es + rollup в их усилиях по созданию пакетов меньшего размера. Также для меня семантически лучше избегать этого - то, что на самом деле библиотеки делают в таких случаях, предоставляет пространство имен, и оно лучше представлено при использовании import * as Namespace from 'namespace'

uglify-es теперь считается стабильным?

Так же стабильно, как и все остальное в экосистеме JS. Более 500 тысяч загрузок в неделю.

это все и хорошо в мире свертки, но учитывая, что React связан в основном с приложениями, а те, которые используют в основном webpack, по умолчанию не поднимают область видимости

Во всяком случае, это вариант. В любом случае значения по умолчанию для Webpack не идеальны - вы должны использовать ModuleConcatenationPlugin как вы знаете.

Добавляем сюда несколько центов:

  • Я полностью согласен с @ Rich-Harris, что семантически именованный экспорт является правильным выбором.
  • Мне действительно не нравятся import React from 'react' или import * as React from 'react' только за то, чтобы иметь возможность использовать синтаксис JSX. На мой взгляд, этот дизайн явно нарушает принцип разделения интерфейса, поскольку он вынуждает пользователей импортировать весь React только для того, чтобы иметь возможность использовать часть createElement (хотя, по общему признанию, с экспортом пространства имен такой пакет, как Rollup, будет снова удалите ненужный экспорт)

Поэтому, если мы находимся в точке, где мы можем принимать решения о критических изменениях, я бы посоветовал изменить это так, чтобы JSX зависел от одной (глобальной или импортированной) функции. Я бы назвал его createJSXElement() , что, на мой взгляд, описывает его даже лучше, чем createElement() и больше не нуждается в контексте React, чтобы иметь смысл. Но в мире, где важен каждый байт, jsx() , вероятно, тоже подойдет.

Это также, наконец, отделит JSX от React таким образом, чтобы другие библиотеки могли выбрать поддержку JSX, используя то же преобразование и предоставляя другую функцию jsx . Конечно, вы несете большую ответственность за то, чтобы направлять бесчисленное количество установленных приложений через такую ​​трансформацию, но с архитектурной точки зрения, я думаю, что именно здесь должны двигаться React и JSX. Использование Babel для выполнения тяжелой работы по такой трансформации звучит для меня как отличная идея!

Лично я не вижу большой выгоды в переходе на jsx helper, поскольку IMHO по умолчанию для плагина babel должен импортировать его из пакета react , поэтому имя фактического помощника на самом деле не дело - остальное просто конфигурируемо.

Это, вероятно, немного не соответствует основному обсуждению, но мне любопытно, насколько хорошо модули ES работают с проверкой process.env.NODE_ENV для условного экспорта пакетов dev / prod? Например,

https://github.com/facebook/react/blob/d9c1dbd61772f8f8ab0cdf389e70463d704c480b/packages/react/npm/index.js#L3 -L7

Возможно, мне здесь не хватает чего-то очевидного, но я изо всех сил пытаюсь понять, как перевести этот шаблон в модули ES?

@NMinhNguyen Условный экспорт невозможен с модулями ES.

process.env.NODE_ENV проверки могут быть на более детальном (кодовом) уровне, готовые к замене сборщиком с соответствующими значениями.

@Andarist @milesj Спасибо, что подтвердили мои подозрения :)

process.env.NODE_ENV проверки могут быть на более детальном (кодовом) уровне, готовые к замене сборщиком с соответствующими значениями.

Из сообщения в блоге React 16 я подумал, что проверки process.env.NODE_ENV были вытащены на самый верх намеренно (в отличие от того, что они более детализированы, что и есть в исходном коде, если я не ошибаюсь ), чтобы повысить производительность в Node.js?

Улучшенный рендеринг на стороне сервера

React 16 включает полностью переписанный серверный рендерер. Это действительно быстро. Он поддерживает потоковую передачу , поэтому вы можете быстрее отправлять байты клиенту. И благодаря новой стратегии упаковки, которая компилирует проверки process.env (вы не поверите, чтение process.env в Node очень медленное!), Вам больше не нужно связывать React, чтобы получить хороший сервер. производительность рендеринга.

Мол, я не уверен, как можно использовать поле module в package.json и различать dev / prod для ESM, сохраняя при этом пакеты ES плоскими и не влияя на производительность Node.js

Например, я не уверен, как можно использовать поле модуля в package.json и различать dev / prod для ESM, сохраняя при этом пакеты ES плоскими и не влияя на производительность Node.js

Это, безусловно, недостаток, потому что на данный момент нет стандартного способа сделать это. OTOH - это всего лишь вопрос инструментов, можно (и это довольно просто) скомпилировать это на этапах сборки вашего приложения даже сегодня. Конечно, было бы проще, если бы пакет мог предоставлять сборки dev / prod, а преобразователь просто знал бы, какую из них выбрать, но, возможно, это просто вопрос доведения этой идеи до авторов инструментов.

Для класса:

import Component from 'react/Component'

class MyButton extends Component{
  constructor(){
    this.state = {}
  }

  render() {
    return <button> Button <Button>
  }
}

Где преобразование будет использовать super.createElement () для преобразования в jsx или использовать статический Component.createElement ().

Для компонентов без состояния:

import jsx from 'react/jsx'

const MyButton = () => jsx`<button> Button <Button>`;

возможно, можно использовать тегированный литерал шаблона?

Узел, надеюсь, примет этот PR https://github.com/nodejs/node/pull/18392

Здесь мы согласны с @ Rich-Harris.

Просто оставьте комментарий к этой ветке, который на самом деле не упоминался конкретно.

Я нахожусь в ситуации, когда я вообще не использую сборщик и просто хочу импортировать реакцию и различные компоненты для использования непосредственно через браузер ( <script type="module" src="..."> ), т.е.

import React from “https://unpkg.com/[email protected]/umd/react.development.js”;
import ReactDOM from “https://unpkg.com/[email protected]/umd/react-dom.development.js”;
ReactDOM.render(
  React.createElement(...),
  document.getElementById('root')
);

Насколько я могу судить, сегодня это невозможно. Вместо этого я должен включить UMD-версию реакции через тег <script> из CDN, а затем предположить, что он присутствует в окне в любом модуле <script type="module"> я пишу:

// myPage.html
<div id="myComponentRoot"></div>
<script src="https://unpkg.com/[email protected]/umd/react.development.js"></script>
<script type="module" src="/assets/scripts/components/MyComponent.js"></script>

// MyComponent.js
import AnotherComponent from "/assets/scripts/components/AnotherComponent.js";
window.ReactDOM.render(
  window.React.createElement(AnotherComponent),
  document.getElementById('root')
);

// AnotherComponent.js
export default class AnotherComponent extends window.React.Component {...}

Было бы фантастически иметь импортирование реакции из CDN. Это сделало бы прототипирование в браузере очень быстрым и легким, сохраняя при этом возможность разделения файлов. Одна вещь, которой я всегда чувствовал, что я жертвую при использовании React без сборщика, - это возможность разделять компоненты (и другие служебные функции и т. Д.) По файлам. Но теперь, когда браузер поддерживает собственные модули ES, я могу писать свои компоненты React в отдельных файлах, а браузер просто потребляет их по мере написания. Конечно, если я не использую JSX, но даже если бы я использовал JSX, я мог бы получить все файлы на месте с помощью шага сборки, и весь мой импорт по-прежнему работал бы в браузере.

// /assets/scripts/entry.js
import React from “https://unpkg.com/[email protected]/umd/react.development.js”;
import React from “https://unpkg.com/[email protected]/umd/react-dom.development.js”;
import RelatedPosts from "/assets/scripts/components/RelatedPosts.js";
ReactDOM.render(
  React.createElement(RelatedPosts),
  document.getElementById('root')
);

// /assets/scripts/components/RelatedPosts.js
import React from “https://unpkg.com/[email protected]/umd/react.development.js”;
import ListItem from "/assets/scripts/components/ListItem.js"
export default class MyComponent extends React.Component {
  componentDidMount() { /* fetch some data */ }
  render() { 
    return React.createElement(
      'ul',
      {},
      this.state.items.map(item => React.createElement(ListItem, { item: item })
    )
  }
}

// /assets/scripts/components/ListItem.js
import React from “https://unpkg.com/[email protected]/umd/react.development.js”;
export default function ListItem(props) {
  return React.createElement('li', null, ...)
}

Я уверен, что некоторые люди будут возражать, если постоянно набирать этот URL-адрес CDN - это проблема (проблема, которую некоторые люди пытаются решить), но компромисс того стоит. Изменение / обновление этого URL-адреса - это простой поиск / замена. Для моего случая использования это перевешивает проблему настройки сборщика пакетов.

Если бы у React была поддержка чего-то вроде этого, не было бы необходимости в инструментах. Я просто использую браузер. Я мог бы добавить такой код в несколько личных проектов, которые предполагают современные браузеры и используют реакцию как прогрессивное улучшение страницы. Что делает это фантастическим, так это то, что когда я возвращаюсь к базе кода через 12 месяцев, мне не нужно менять кучу API-интерфейсов инструментов или даже использовать NPM в качестве диспетчера пакетов. Я просто использую API из браузера, ничего больше.

FWIW: если / когда React действительно поставляется с такой поддержкой, я думаю, было бы очень полезно показать, как вы можете использовать React, как это, в документации, обучая, что вы можете использовать React и его компонентную модель, разделяя логику каждого компонента через его собственный файл, и для этого вам не нужен сборщик, просто используйте собственный <script type="module"> , импортируйте React из CDN (или вашей собственной локальной копии) и готово! »

Теперь все современные браузеры, включая мобильные версии, поддерживают ESM. ESM больше не является модульной системой будущего, а является действующим стандартом де-факто.

Имейте в виду, что непредоставление стандартизованного модуля является серьезной проблемой, особенно для стандартной веб-библиотеки.

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

Это типичный код для применения библиотек React, и на самом деле нет библиотек, которые можно импортировать, вместо этого сторонние транспилеры и сборщики эмулируют процесс импорта.

Непредоставление настоящего ESM было немного оправдано, поскольку браузеры в любом случае не поддерживали собственный ESM, но, очевидно, время истекло, и теперь пора предоставить ESM, как указано в типичном примере кода для import .

Я начал работать над этим здесь и здесь

@TrySound Спасибо за ваш вклад.
Есть ли где взять и протестировать сборку ESM?

Готово только для пакета response-is.

@TrySound
Хорошо, я нашел вашу ветку https://github.com/TrySound/react/tree/react-is-esm и построил, и теперь я знаю, что вы имели в виду. С нетерпением жду и react-dom .

Я думаю, что сообщество React достаточно долго обсуждало этот вопрос.
https://discuss.reactjs.org/t/es6-import-as-react-vs-import-react/360/

Пожалуйста, определитесь с официальной спецификацией модуля ES6 и опубликуйте ее в ближайшее время.

@kenokabe Мы уже в пути. Не заставляйте нас, пожалуйста. Это не так просто.

Текущий план - это перенос всех пакетов только с именованными экспортами. Это изменение не повлияет на код библиотек и не должно вносить критических изменений, поскольку документы также используют именованный экспорт.

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

@TrySound Мои извинения.
Я не имел в виду вас, поскольку главное упоминание этой темы -

Мы не можем легко это сделать, потому что мы еще не решили, как будет выглядеть экспорт ES верхнего уровня из каждого пакета. Например, есть ли у React набор именованных экспортов, а также экспорт по умолчанию под названием React? Следует ли поощрять импорт * для лучшего тряски деревьев?

Упомянутый день произошел недавно, и я просто подумал, что это обсуждалось в сообществе React, поэтому я хотел предположить, что решение будет ясным. Спасибо!

Хочу получить обновленную информацию об этом ...

Я использую webpack v4 для объединения нашего приложения, в то время как моя IDE intellisense (WebStorm) предлагает мне использовать import * as React from 'react'; то время как мой коллега просит меня изменить import React from 'react'; при проверке кода. Оба работают нормально, поэтому я подумал, что он говорит какую-то ерунду, но, чтобы порадовать его, я все равно меняю это. Вот как я нахожу эту ветку.

Из любопытства я сравниваю различия в размере окончательной сборки между ними (с React 16.8.1):

В import * as React from 'react'; : 6 618 723 байта
В import React from 'react'; : 6 619 077 байт

Итак, очевидно, что у него были некоторые отличия, хотя и незначительные. (примечание. Я проделал то же самое с propTypes )

Если я правильно понимаю в этой теме, было бы лучше иметь import * as React from 'react'; , верно ?! Потому что (1) да, это немного сэкономило размер; (2) ESM - это стандартизованный способ, поэтому никаких остатков CJS. Если это так, я хотел бы изменить это сегодня и согласовать с моей IDE.

@leoyli В долгосрочной перспективе да. Но сначала будут и именованный экспорт, и экспорт по умолчанию, чтобы не нарушить существующий код.

Я взял дело в свои руки здесь, что-то вроде эксперимента, поскольку я больше не использую сборщик в своих проектах и ​​хотел бы по-прежнему использовать реакцию (прямо с unpkg.com, как вы можете с другими библиотеками, такими как Vue, Hyperapp и т. Д.) . Это то, что я придумал, ничего особенного, просто отредактированный вручную umd:

https://github.com/lukejacksonn/es-react

Модуль ES6, отображающий последнюю версию React и ReactDOM

Как описано в README, это в основном POC, но для людей, которые не могут дождаться, пока эта сборка приземлится, это сборка 16.8.3 которая включает в себя хуки, неопределенность, ленивость и т. Д. И работает как ожидалось, выполняя:

import { React, ReactDOM } from 'https://unpkg.com/es-react'

Возможно, некоторые из вас в этой теме сочтут это полезным ... Лично я использовал этот подход для создания стартового проекта без этапов сборки . Это также все еще продолжается.

@lukejacksonn Мы также использовали такое решение в продакшене, хотя наш подход отличался в том смысле, что это скорее скрипт-преобразователь для UMD-версии React и ReactDOM в вашем текущем проекте. И что он выводит этот файл отдельно, поэтому для большей части кода его следует заменить. Если вам интересно https://github.com/wearespindle/react-ecmascript, вы также можете загрузить его из unpkg https://unpkg.com/react-ecmascript/

@ PM5544 Ого .. это гораздо более комплексное решение, чем мое! Отличная работа 💯

Потрясающая штука @ PM5544. Хотел бы когда-нибудь услышать об этом больше. Может быть, снова в гостях у Xebia?
Недавно я использовал pack для объединения своих пакетов с открытым исходным кодом, которые поддерживают UNPKG.
Кто-нибудь знает хорошую статью о загрузке зависимостей из UNPKG напрямую, а не с использованием сборщика?

В настоящее время я пишу его и в июне тоже сделаю доклад на React Norway!

@TrySound Были ли обновления по этому

Это необходимо сначала объединить https://github.com/facebook/react/pull/15037

@TrySound Хорошо, спасибо, я направил свое предложение о помощи в эту ветку.

Когда вы идете с экспортом React по умолчанию, вы можете использовать следующий подход:

// react/index.js
import * as React from "./react";
export { React as default }
export * from "./react";

// react/react.js
export function createElement() {}
...

Это делает возможным статический анализ того, что экспорт по умолчанию представляет собой объект пространства имен, который позволяет встряхивать деревья для этих конструкций в webpack 5 и rollup:

import React from "react";

React.createElement(); // <- only `createElement` export is used

Я нахожусь в чате накопительного гиттера уже 1,5 года, и такого рода проблемы возникают каждые 2 недели или около того ...

Кстати, @lukejacksonn проделал огромную работу над неофициальным форком es- react, очень рекомендую его.

Кстати, @lukejacksonn проделал огромную работу над неофициальным форком es- react, очень рекомендую его.

Интересно, почему официальная команда FB ничего не делает с этим.

Я также надеялся, что это вызовет некоторое давление для продвижения вперед, и, думаю, сам @lukejacksonn тоже. Однако, насколько я понимаю, он действительно получил некоторую поддержку от команды React для создания своего форка из исходных источников, так что, похоже, есть хоть какой-то интерес к этому.

Я тоже на это надеялся. На самом деле я практически не получил поддержки от группы реагирования при создании пакета (кроме некоторых добрых слов поддержки от @threepointone). Недавно коллега из Formidable помог мне создать пакет программно, что является улучшением по сравнению с выполнением этого вручную каждый раз, когда выпускается новая версия реакции, но приводит к гораздо менее чистому выводу на вкладке сети, поэтому я не уверен, будет ли он оставайся так же. Мы увидим!

Сейчас почти 2020 год, я хотел бы знать, есть ли обновления от официальной команды FB? Будут ли связанные с этим изменения в React v17?

Тем, кому нужен обновленный ES-модуль React NOW, попробуйте @pica/react , который уже есть на v16.13.x
https://www.npmjs.com/package/@pika/react
https://www.npmjs.com/package/@pika/react -dom
https://github.com/pikapkg/react

Может быть, в далеком будущем.

Хорошо, уже в будущем. Это в ближайшем будущем сейчас?

@gaearon, что

Судя по всему, такое решение уже давно принято: № 18102. Этот вопрос можно закрыть сейчас.

Я дам небольшое обновление по этому поводу.

Нет экспорта по умолчанию

В конечном итоге мы планируем полностью отказаться от экспорта по умолчанию:

import { useState } from 'react';

В том мире это не сработало бы:

import React from 'react'; // no

Это сработает, хотя это немного шумно:

import * as React from 'react';

Но вот что самое интересное. На самом деле не было бы особых причин для импорта React .

Автоматический импорт JSX

Причина, по которой люди сегодня импортируют React в основном связана с JSX. Но @lunaruan заканчивает работу над новым преобразованием JSX и соответствующими кодами, которые устраняют необходимость в нем.

Итак, вы бы пошли от этого:

import React from 'react';
import { useState } from 'react';

function Button() {
  const [pressed, setPressed] = useState(false)
  return <button />
}

к этому:

import { useState } from 'react';

function Button() {
  const [pressed, setPressed] = useState(false)
  return <button />
}

JSX автоматически вставляет правильный импорт под капотом, поэтому нет необходимости в React в области видимости.
Это то, что делает приемлемым шаг по удалению экспорта по умолчанию. Просто они вам не нужны.

Модули ES

Развертывание ESM за пределами небольшой группы энтузиастов является сложной задачей. Обширная экосистема на самом деле не готова, и есть масса причин, по которым что-то идет не так с различными комбинациями инструментов. Способ взаимодействия CJS и ESM очень сложен, и это взаимодействие (и то, как оно терпит неудачу) является источником большинства этих проблем.

В настоящее время мы думаем, что когда мы переходим к ESM, мы, возможно, захотим попробовать использовать ESM полностью. Никакого CJS вообще - или отдельно в совместимом устаревшем пакете. Этого не произойдет в React 17 и маловероятно в 18, но можно попробовать в React 19.

Для тех, кто ищет альтернативу сборке ESM @pika/react , посетите https://github.com/esm-bundle/react и https://github.com/esm-bundle/react-dom. Разница в том, что их можно использовать в браузерах без полифила карты импорта - import React from 'react'; внутри исходного кода response-dom изменен для импорта React с полного URL-адреса CDN. Другое отличие состоит в том, что новые версии публикуются автоматически при публикации новой версии React, без каких-либо ручных действий.

Демонстрация тестовой среды кода: https://codesandbox.io/s/gifted-roentgen-qcqoj?file=/index.html

Некоторые люди оспаривают мнение о том, что экосистема не готова. У меня нет полного контекста по этому поводу, но если вы считаете, что сейчас хорошее время, чтобы начать вносить изменения, я был бы признателен, если бы вы могли просмотреть https://github.com/reactjs/rfcs/pull/38 и выразить любые опасения по этому поводу. Вы примерно это имели в виду или предпочли бы другой подход?

Но вот что самое интересное. На самом деле не было бы особых причин для импорта React.

Есть, если вы используете TypeScript, и будет продолжать это делать в обозримом будущем. Пока TS не узнает об этом новом волшебном поведении babel, разработчикам придется продолжать явно импортировать React, и им нужно знать, что такое правильный оператор импорта.

JSX автоматически вставляет правильный импорт под капот, поэтому React не нужен в области видимости.

s / JSX / Новый плагин jsx transform для babel react /. JSX - это расширение синтаксиса JavaScript, само по себе ничего не делает.

Есть, если вы используете TypeScript, и будет продолжать это делать в обозримом будущем. Пока TS не узнает об этом новом волшебном поведении babel, разработчикам придется продолжать явно импортировать React, и им нужно знать, что такое правильный оператор импорта.

Команда TypeScript знает об этом изменении, и оно отслеживается в https://github.com/microsoft/TypeScript/issues/34547. Мы находимся в тесном контакте с ними, поэтому будьте уверены, что это для нас не второсортный гражданин.

s / JSX / Новый плагин jsx transform для babel react /

Да вот что я имел в виду!

Буду признателен, если вы можете просмотреть responsejs / rfcs # 38 и выразить любые сомнения по этому поводу. Вы примерно это имели в виду или предпочли бы другой подход?

Часть исходного текста RFC устарела. NodeJS позволяет запускать ESM в файлах .js вместо .mjs сейчас, когда вы указываете "type" в своем package.json. ( документы ).

В частности, в исходном тексте RFC говорится следующее, что неверно:

Код ESM должен находиться внутри файла .mjs

После разговора с @frehner вот наше предложение о том, как можно постепенно преобразовать React в ESM. Обратите внимание, что мы не связываем проблему именованного экспорта / экспорта по умолчанию с публикацией ESM-версии React. @gaearon пояснил, что экспорт по умолчанию в конечном итоге исчезнет, ​​но это не указано в нашем предложении до фазы 4.

| | Фаза 1 | Фаза 2 | Фаза 3 | Фаза 4 |
| - | -------- | -------- | -------- | ------- |
| ESM опубликовали? | ✔️ | ✔️ | ✔️ | ✔️ |
| package.json "module" | ❌ | ✔️ | ✔️ | ✔️ |
| webpack / rollup используют esm 1 , 2 | ❌ | ✔️ | ✔️ | ✔️ |
| package.json "exports" | ❌ | ❌ | ✔️ | ✔️ |
| package.json "type" | ❌ | ❌ | ✔️ | ✔️ |
| NodeJS использует esm | ❌ | ❌ | ✔️ | ✔️ |
| Критическое изменение? | ❌ | ❌ | ❓ | ✔️ |
| Экспорт по умолчанию ушел? | ❌ | ❌ | ❌ | ✔️ |
| Расширения файлов, необходимые при импорте | ❌ | ❌ | ❌ | ❌ |
| расширения файлов mjs | ❌ | ❌ | ❌ | ❌ |

Я думаю, что есть веский аргумент в пользу объединения Фазы 1 и Фазы 2 вместе, поскольку Фаза 1 на самом деле предназначена только для энтузиастов. Тем не менее, я разделил их, потому что считаю, что разделение фаз дает возможность очень медленно развернуть ESM таким образом, чтобы не сразу нарушить CRA и всю экосистему, не дав ранним последователям возможность сообщить о проблемах и найти какие-либо исправления. .

@joeldenning, каковы будут приблизительные сроки фаз? это просто временная привязка или это связано с некоторыми временными точками в экосистеме? Будет ли require('react') прежнему работать на этапе 3?

каковы были бы приблизительные сроки фаз? это просто временная привязка или это связано с некоторыми временными точками в экосистеме?

Сроки для всех этапов ограничены только тем, что нужно сделать, и графиком выпуска React. Насколько я понимаю, все предлагаемые вещи поддерживаются сборщиками и nodejs с соответствующими запасными вариантами при использовании более старых версий, которые их не поддерживают.

Будет ли require ('реагировать') работать на Фазе 3?

Я думаю, что да. См. Https://nodejs.org/api/esm.html#esm_dual_commonjs_es_module_packages и https://nodejs.org/api/esm.html#esm_package_entry_points. Конечно, вполне возможно, что я не знаю всех угловых случаев для всего, но я понимаю, что предложенный мной путь перехода «будет работать везде». Может быть, это известные последние слова 😄

Дополнительное пояснение: вот как мы предлагаем опубликованный tarball-файл для React:

node_modules/react/
  cjs/
    react.development.js
    react.production.min.js
    react.profiling.min.js
  umd/
    react.development.js
    react.production.min.js
    react.profiling.min.js
  esm/
    react.development.js
    react.production.min.js
    react.profiling.min.js
  index.js

А вот пример того, каким будет package.json в конце:

{
  "type": "module",
  "main": "index.js",
  "module": "esm/react.development.js",
  "exports": {
    "import": "./esm/react.development.js",
    "require": "./cjs/react.development.js"
  }
}

^ Это не полностью продумано и доведено до совершенства, но я делюсь, чтобы дать конкретный контекст предложения.

Итак - React страдает от опасности двойного пакета, потому что он сохраняет состояние (хуки), поэтому это состояние должно быть тщательно изолировано. Либо он должен находиться в небольшом файле CJS, который может быть импортирован как записями CJS, так и ESM, либо, возможно, есть способ загрузить .json и изменить его с этим состоянием из обеих записей (я не уверен на 100% в второй подход).

Я также думаю, что важно указать в вашей таблице добавление именованных экспортов, что произойдет уже на этапе 1.

Я до сих пор не уверен на 100%, не пропустил ли я несколько угловых случаев, это очень сложная тема. В то же время график должен быть привязан к контрольным точкам экосистемы - этап 3 может быть отправлен только после того, как exports получит достаточную поддержку в сборщиках, в противном случае я считаю, что это может привести к потенциальным проблемам.

React страдает от опасности двойного пакета, потому что он сохраняет состояние (хуки), поэтому это состояние необходимо тщательно изолировать.

Хороший момент, я не подумал об этом ( подробнее ). В этом случае, возможно, будет полезно ваше предложение об общем файле между сборками CJS / ESM. Или, возможно, версия ESM не является полной копией response, а просто общедоступным интерфейсом ESM, который вызывает сборку CJS.

Я также думаю, что важно указать в вашей таблице добавление именованных экспортов, что произойдет уже на этапе 1.

Кажется, что это уже так в исходном коде? Насколько я могу судить, исходный код уже экспортирует вещи как именованные экспорты.

https://github.com/facebook/react/blob/ef22aecfc52cdf0d7cedc723c590719c009c2a64/packages/react/index.js#L39

https://github.com/facebook/react/blob/ef22aecfc52cdf0d7cedc723c590719c009c2a64/packages/react/index.js#L39

Я до сих пор не уверен на 100%, не пропустил ли я несколько угловых случаев, это очень сложная тема. В то же время график должен быть привязан к контрольным точкам экосистемы - этап 3 может быть отправлен только после того, как экспорт получит достаточную поддержку в сборщиках, в противном случае я считаю, что это может привести к потенциальным проблемам.

Согласен с Фазой 3 - вот почему я поставил вопрос, было ли это критическим изменением. Я знаю, что добавление экспорта package.json часто было критическим изменением для других пакетов в экосистеме. И тема определенно сложная. Следует отметить, что при желании порядок этапов 3 и 4 можно поменять местами. Я думаю, что тем, кто реализует каждую фазу, придется провести очень, очень тщательное тестирование многих версий webpack, rollup, nodejs и т. Д. Я не говорю, что работа, которую нужно выполнить, тривиальна - просто говорю, что я действительно думаю, что есть здесь скорее всего переходный путь :)

Кажется, что это уже так в исходном коде? Насколько я могу судить, исходный код уже экспортирует вещи как именованные экспорты.

Ах, верно, в этом случае должна быть строка таблицы для добавления export default 😂, поскольку она в настоящее время работает в большинстве сборщиков пакетов и популярна в дикой природе, но добавление истинной записи ESM без предоставления default будет сломать эти обычаи.

в этом случае должна быть строка таблицы для добавления экспорта по умолчанию

Да, хорошее замечание. Я добавлю это. Я думаю, что PR, делающий это, «покажется плохим», но я рассматриваю это как просто принятие текущего публичного интерфейса таким, какой он есть, с планами по улучшению в будущем.

Следует отметить, что как только какая-либо версия будет опубликована, пути назад уже не будет, и любые семантические изменения в ней будут нарушены. Поскольку мы не очень часто выпускаем крупные компании, нам следует подумать о том, как пакетировать любые изменения, чтобы уменьшить отток.

Также возникает вопрос о том, как будет обрабатываться разделение сборки на разработку и производство. И действительно, очень важно сохранить состояние сборки React.

Следует отметить, что как только какая-либо версия будет опубликована, пути назад уже не будет, и любые семантические изменения в ней будут нарушены. Поскольку мы не очень часто выпускаем крупные компании, нам следует подумать о том, как пакетировать любые изменения, чтобы уменьшить отток.

Хорошие моменты. Моя идея состоит в том, чтобы добавить явный export default React в сборку ESM, которая опубликована в React 16. Я думаю, что PR может вызвать недоумение и вызвать споры, поскольку это не пункт назначения, который был определен. Однако я считаю, что мы можем создать ESM-сборку в React 16, а затем удалить export default в будущей основной версии. Для меня использование реакции через import React from 'react'; настолько распространено, что экспорт по умолчанию в сборке ESM означает просто «принятие того, где мы находимся». В будущей основной версии он будет удален.

Также связано с минимизацией критических изменений - фазы 3 и 4 могут быть частью одного и того же основного выпуска. Возможно, что этап 3 может быть выполнен полностью обратно совместимым способом, и в этом случае он также может проникнуть в выпуск 16. Но это сложнее, и я недостаточно знаю о нем, чтобы быть уверенным в этом.

Также возникает вопрос о том, как будет обрабатываться разделение сборки на разработку и производство.

Это то, что я упустил из виду. Я не знаю, как это сделать с помощью ESM как в сборщиках, так и в NodeJS, но проведу небольшое исследование, чтобы увидеть, что возможно. Нашел это мертвое предложение , а вот живые посмотрю :)

И действительно, очень важно сохранить состояние сборки React.

Согласовано. Следует отметить, что состояние сборки React с отслеживанием состояния необходимо решать только на этапе 3, а не на этапах 1 и 2. Варианты, которые мы с @Andarist предложили,

Самый простой и обратно совместимый подход на данный момент - это добавить простую оболочку ESM, чтобы разрешить именованный импорт.

Я был бы счастлив сделать PR, чтобы добавить это в React, гарантируя, что добавление поля «экспорт» не является критическим изменением (я написал инструмент npx ls-exports , который упрощает определение этого). Это не поможет людям, пытающимся использовать ESM без процесса сборки, но это проблема, которую отдельные пакеты в любом случае не могут решить.

@gaearon будет ли это полезно? Он мог появиться в React 16 как семвер-минор.

Сборка CJS продолжит использовать обнаружение среды, как и сейчас, так что (сложная, нерешенная) проблема в ESM еще не должна быть выяснена.

Однако я считаю, что мы можем создать сборку ESM в React 16, а затем удалить экспорт по умолчанию в будущей основной версии.

Еще одна проблема заключается в том, что увеличение количества конфигураций создает нагрузку на экосистему. Например, я думаю, что если мы выпустим сборку ESM, она не должна включать что-то, что мы определенно собираемся удалить в следующем выпуске, например экспорт по умолчанию. Другими словами, я не думаю, что имеет смысл вводить что-то, что уходит (экспорт по умолчанию), во что-то, что только что добавляется (сборка ESM).

Однако я считаю, что мы можем создать сборку ESM в React 16, а затем удалить экспорт по умолчанию в будущей основной версии.

Еще одна проблема заключается в том, что увеличение количества конфигураций создает нагрузку на экосистему. Например, я думаю, что если мы выпустим сборку ESM, она не должна включать что-то, что мы определенно собираемся удалить в следующем выпуске, например экспорт по умолчанию. Другими словами, я не думаю, что имеет смысл вводить что-то, что уходит (экспорт по умолчанию), во что-то, что только что добавляется (сборка ESM).

Я думаю, это зависит от того, как долго продлится следующий релиз. Если мы говорим о разнице в неделю, это кажется разумным. Однако, если мы говорим о месяцах / годах, то я не понимаю, почему было бы плохо выпустить это за такое количество времени.

В данном случае проблема заключается в том, что чем дольше он существует, тем больше людей зависит от него в текущей форме. А внесение в них критических изменений усложнило бы им обновление React. Теперь существует не одна миграция ESM, а несколько, и некоторые люди останутся позади, потому что они прыгнули слишком рано и позже не имеют ресурсов для другой миграции. Что, в случае ESM, будет волновать всю экосистему.

Я думаю, что если мы выпустим сборку ESM, она не должна включать что-то, что мы обязательно удалим в следующем выпуске, например экспорт по умолчанию. Другими словами, я не думаю, что имеет смысл вводить что-то, что уходит (экспорт по умолчанию), во что-то, что только что добавляется (сборка ESM).

Я не рассматриваю import React from 'react' как что-то недавно добавленное, а скорее как принятие текущей реальности. Несмотря на то, что это, возможно, было непреднамеренным и только побочным эффектом устаревшего взаимодействия ESM / CJS, все еще существуют тысячи (миллионы?) Строк кода, которые делают это. Альтернатива (только экспорт именованных экспортов) говорит пользователям: «мы опубликовали сборку ESM в младшей версии, но вы не можете использовать ее, не изменив весь свой код», что для меня больше сбивает с толку пользователей, чем просмотр «удаленного экспорта по умолчанию» в примечаниях к выпуску основной версии.

Мне любопытно - как можно добавить ESM с экспортом по умолчанию обратно совместимым способом? Это уже возникало раньше (например, https://github.com/facebook/react/pull/18187, который также ссылается на связанные проблемы). Проблема заключается в взаимодействии Webpack CJS <-> ESM, где, если у вас есть код CJS, выполняющий require('react') какой веб-пакет вернет при наличии ESM react с экспортом по умолчанию, это объект с default свойство (то есть теперь требуется require('react').default ) независимо от CJS react . Но, может быть, если вы также экспортируете named, тогда это не будет проблемой? Я думаю, что @TrySound раньше сталкивался с такими проблемами в других пакетах.

Но, может быть, если вы также экспортируете named, тогда это не будет проблемой?

Да, это тот подход, о котором я думаю. См. Последние 40 строк https://unpkg.com/browse/@esm-bundle/react @ 16.13.1 / esm / react.development.js - это неофициальная версия React, которая делает именно это и используется несколькими организациям в качестве замены официальному React. Никаких критических изменений.

Но, может быть, если вы также экспортируете named, тогда это не будет проблемой?

Да, это тот подход, о котором я думаю. См. Последние 40 строк https://unpkg.com/browse/@esm-bundle/react @ 16.13.1 / esm / react.development.js - это неофициальная версия React, которая делает именно это и используется несколькими организациям в качестве замены официальному React. Никаких критических изменений.

Но вы потребляете его из кода CJS или ESM? Потому что это проблемы взаимодействия CJS <-> ESM, которые могут быть очень удивительными.

@gaearon, чтобы быть ясным; имеет смысл не иметь экспорта по умолчанию в предлагаемой мной оболочке ESM; любой, кто использует собственный ESM, сделает import * as React from 'react' чтобы обойти это. Однако справедливо, что любой, кто делает это сейчас, сочтет критическим изменением внезапное отсутствие экспорта по умолчанию, поэтому ему придется подождать до версии 17, если вы не хотите добавлять значение по умолчанию сейчас.

Но вы потребляете его из кода CJS или ESM? Потому что это проблемы взаимодействия CJS <-> ESM, которые могут быть очень удивительными.

Я успешно импортировал его в webpack из файлов CJS и ESM.

любой, кто занимается нативным ESM, сделает import * как React из response, чтобы обойти это. Однако справедливо, что любой, кто делает это сейчас, сочтет критическим изменением внезапное отсутствие экспорта по умолчанию, поэтому ему придется подождать до версии 17, если вы не хотите добавлять значение по умолчанию сейчас.

Согласовано. Если начинать с нуля, экспорт по умолчанию не нужен. Однако без добавления экспорта по умолчанию невозможно реализовать Фазу 2 без критического изменения. Мне лично было бы хорошо, если бы я сделал Фазу 1 в react 16 и Фазы 2-4 в React 17+, хотя я предпочитаю выполнить Фазу 1, 2 и, возможно, даже 3 (с помощью инструмента проверки экспорта @ljharb ) в React 16 без критических изменений. Причина в том, что фаза 2 является большой, когда большинство пользователей начинают использовать пакет ESM, тогда как фаза 1 в основном предназначена для ранних последователей / энтузиастов.

Продолжая это предложение . Похоже, это предложение о переходе на двойной пакет с полным исходным кодом CJS и ESM, имеющим дело с опасностью двойного пакета путем изолирования состояния где-то еще ( подход 2 ). В отличие от использования оболочки ESM, как в моем предыдущем RFC ( подход 1 ).

Если допустить это, у меня есть несколько заметок о вещах, которые еще не были упомянуты.

  • У вас не может быть пакета Node.js, в котором файлы ESM и CommonJS используют .js . Если вы установите "type": "module" тогда все файлы CommonJS должны использовать вместо этого расширение .cjs .
  • Риск двойного пакета «государство и равенство» - не единственная проблема, связанная с наличием и CJS, и ESM. Наличие потенциально двух загруженных версий одного и того же пакета также увеличивает объем памяти, занимаемый React в этих случаях, и React не является маленькой библиотекой. Это не является препятствием для сделки, но об этом стоит помнить.
  • Я действительно вижу потенциальную опасность двойного пакета помимо внутреннего состояния React. Например, если реализация класса Component не является частью кода CJS, где мы разделяем состояние, а вместо этого является частью пакетов CJS / ESM, тогда существует риск проверки instanceof Component в различных библиотеках. ломка.

У вас не может быть пакета Node.js, в котором файлы ESM и CommonJS используют .js. Если вы установите "type": "module", тогда все файлы CommonJS должны использовать вместо этого расширение .cjs.

Это верно для NodeJS (но не для сборщиков), поэтому файлы в каталоге cjs должны заканчиваться на .cjs .

Наличие потенциально двух загруженных версий одного и того же пакета также увеличивает объем памяти, занимаемый React в этих случаях.

Я вижу, как это увеличивает размер публикуемого архива до npm и, следовательно, общий размер на диске. Но я не понимаю, как это влияет на память. Насколько мне известно, сборщики пакетов и NodeJS не переносят в память код, который не был загружен через import / require() . Не могли бы вы уточнить, как изменится объем памяти?

Например, если реализация класса Component не является частью кода CJS, в котором мы разделяем состояние, а вместо этого является частью пакетов CJS / ESM, тогда существует риск того, что instanceof проверки компонентов в различных библиотеках сломаются.

Одно из предлагаемых решений ( 1 , 2 ) состоит в том, чтобы реализация NodeJS esm была просто интерфейсом ESM, который вызывает код CJS. Таким образом, существует только одно определение Component , которое когда-либо использовалось в Node. Фаза 1 и фаза 2 не повлияют на то, что работает в NodeJS, поэтому это применимо только к фазе 3.

Поскольку мы (webpack) недавно также добавили поддержку поля exports в webpack 5, я хочу отдать свои 2 цента на эту тему:

  • Этот незавершенный документ содержит много информации о поле exports относительно webpack, а также о Node.js и в целом: https://gist.github.com/sokra/e032a0f17c1721c71cfced6f14516c62
  • Вот ключевые моменты по сравнению с Node.js:

    • webpack 5 также добавляет условия development и production , которые очень полезны для реагирования. ( process.env.NODE_ENV , хотя и поддерживается, следует избегать для внешнего кода в целом, это специфично для Node.js)

    • webpack (и другие сборщики) поддерживает require("esm") , что позволяет избежать проблемы двойного состояния, всегда используя ESM (даже для require() ). В webpack для этого введено специальное условие: module . Для этой версии CommonJs и ESM необходимо экспортировать один и тот же интерфейс. В настоящее время нет ничего другого, у кого есть проблема двойного состояния, кроме Node.js. Я не ожидаю, что мы что-то увидим в будущем, поскольку это в основном проблема обратной совместимости.

      Для максимальной совместимости я бы рекомендовал следующее:

package.json

{
    "type": "commonjs",
    "main": "index.js",
    "module": "esm/wrapper.js",
    "exports": {
        ".": {
            "node": {
                "development": {
                    "module": "./esm/index.development.js",
                    "import": "./esm/wrapper.development.js",
                    "require": "./cjs/index.development.js"
                },
                "production": {
                    "module": "./esm/index.production.min.js",
                    "import": "./esm/wrapper.production.min.js",
                    "require": "./cjs/index.production.min.js"
                },
                "import": "./esm/wrapper.js",
                "default": "./cjs/index.js"
            },
            "development": "./esm/index.development.js",
            "production": "./esm/index.production.min.js",
            "default": "./esm/index.production.min.js"
        },
        "./index": "./index.js",
        "./index.js": "./index.js",
        "./umd/react.development": "./umd/react.development.js",
        "./umd/react.development.js": "./umd/react.development.js",
        "./umd/react.production.min": "./umd/react.production.min.js",
        "./umd/react.production.min.js": "./umd/react.production.min.js",
        "./umd/react.profiling.min": "./umd/react.profiling.min.js",
        "./umd/react.profiling.min.js": "./umd/react.profiling.min.js",
        "./package.json": "./package.json"
    }
}

esm / package.json

Позволяет использовать .js качестве расширения в этом каталоге. В качестве альтернативы можно использовать .mjs , но это может иметь потенциальные побочные эффекты, когда инструменты проверяют расширение. Так что .js на всякий случай.

{
    "type": "module"
}

esm / wrapper.js

Эта оболочка необходима для Node.js, чтобы избежать проблемы двойного состояния.

import React from "../cjs/index.js";
export const {
    Children,
    Component,
    ...,
    useState,
    version
} = React;
export { React as default };

cjs / index.js

Это используется Node.js, когда условия development и production не поддерживаются.

'use strict';

if (process.env.NODE_ENV === 'production') {
  module.exports = require('./cjs/react.production.min.js');
} else {
  module.exports = require('./cjs/react.development.js');
}

esm / wrapper.development.js (аналогичный esm / wrapper.production.min.js)

Эти оболочки необходимы для Node.js, чтобы избежать проблемы двойного состояния.
Они используются только после того, как Node.js добавит условия development и production .

import React from "../cjs/index.development.js";
export const {
    Children,
    Component,
    ...,
    useState,
    version
} = React;
export { React as default };

index.js

Для обратной совместимости.

module.exports = require('./cjs/index.js');

esm / index.development.js, esm / index.production.min.js

Это используется инструментами, которые поддерживают поле exports , условие module условия production / development .

/* React in ESM format */

// compat for the default exports with support for tree-shaking
import * as self from "./esm/index.development.js";
export { self as default }

Полученные результаты

  • webpack 5: ./esm/index.development.js или ./esm/index.production.min.js
  • просмотр: ./cjs/index.js
  • webpack 4 из .mjs: ./cjs/index.js
  • другие сборщики: ./esm/wrapper.js
  • Node.js (ESM): ./cjs/index.js (требуется) или ./esm/wrapper.js (импорт)
  • Node.js (старый): ./cjs/index.js
  • Node.js (ESM + dev / prod): ./esm/wrapper.development.js или ./esm/wrapper.production.min.js для импорта, ./cjs/index.development.js или ./cjs/index.production.min.js для требования

Примечания

Не существует esm/index.js как условный выбор версии невозможен в ESM без серьезных компромиссов.
Инструменты могут получить полную выгоду от реакции ESM только тогда, когда они поддерживают поле exports , условие module (из-за проблемы двойного состояния) и production / development conditions (из-за проблемы с условным импортом).

Инструменты могут частично выиграть от реакции ESM, если они поддерживают поле module или поле exports .

import { useState } from "react" или import * as React from "react" технически незаконны, если response является модулем CommonJs.
Большинство инструментов по-прежнему поддерживают обратную совместимость, но некоторые нет, например, Node.js.
Итак, в настоящее время единственный способ использовать реакцию, которая действительна везде, это: import React from "react" .
Этот способ должен оставаться поддерживаемым, в противном случае могут быть случаи (например, Node.js 14), когда нет синтаксиса, который действовал бы для реакции сейчас и реагирования после добавления ESM.

Node.js на данный момент отклонил добавление условия development / production для exports .
Это печально, и я все еще надеюсь, что они в конце концов добавят это.
Вот почему поддержка для этого подготовлена ​​в поле exports выше.

@sokra отличная поломка, очень полезно, спасибо!

Один небольшой вопрос:

Node.js на данный момент отклонил добавление условия разработки / производства для экспорта.

я так понимаю, что это все еще работает? https://github.com/nodejs/node/pull/33171, но, возможно, я неправильно понимаю этот PR

[править] вышеупомянутый PR, на который я ссылался, был заменен https://github.com/nodejs/node/pull/34637

[edit2] и теперь объединен с nodejs

Спасибо @sokra , это очень полезные предложения.

Вот варианты, которые я вижу. Кажется, что все они технически возможны, и что решение является скорее стратегическим, чем технической реализацией:

Опция 1

Добавьте export default React в сборку ESM React 17 и удалите ее, как только будет прекращена поддержка CJS для import React from 'react' (возможно, в React 18?).

Вариант 2

Не добавляйте export default React и создавайте сборку ESM React 17 только с именованными экспортами.

Вариант 3

Не публикуйте сборку React 17 ESM. (😢) Перед созданием сборки ESM дождитесь прекращения поддержки import React from 'react'; .

Сравнение

| | Вариант 1 | Вариант 2 | Вариант 3 |
| - | -------- | -------- | -------- |
| Сборка ESM без ссылки | v17 | v17 | v18 + |
| package.json "модуль" (по умолчанию дерево качается) | v17 | v18 + | v18 + |
| package.json "тип" / "экспорт" (NodeJS использует ESM) | v18 + 1 | v18 + | v18 + |

  1. Возможно, можно реализовать тип / экспорт package.json полностью обратно совместимым способом, и в этом случае он может быть частью React 17, если выбран вариант 1.

Я предпочитаю вариант 1, как я объяснил выше. Тем не менее, вариант 2 также мне очень нравится. Вариант 3, конечно, менее увлекателен. Судя по тому, что я собрал в этом выпуске на github, у нас есть техническая экспертиза, чтобы осуществить любое из этих действий (и, возможно, даже труд!).

Первоначальная реакция на этот вопрос заключалась в следующем: почему этот вопрос остается открытым даже через 3 года ? Прочитав часть его, вы поймете, почему это занимает так много времени. Поддержка такой библиотеки, как React, - огромная задача. Итак 🙇🏻

Учитывая недавние новости о React 17, я обновил свой предыдущий комментарий, указав React 17 вместо 16 для любых будущих планов.

Буду признателен за отзывы людей о том, какой из трех вариантов предпочтительнее.

Я думаю, мы можем добавить поле exports в package.json в React 17, мы могли бы, вероятно, также перенести его на предыдущие версии:

{
  "exports": {
    ".": {
      "development": "./esm/react.development.mjs",
      "production": "./esm/react.production.mjs",
      "node": {
        "import": "./esm/react.node.mjs",
        "require": "./index.js"
      },
      "default": "./index.js"
    },
    "./jsx-dev-runtime": {
      "development": "./esm/react-jsx-dev-runtime.development.mjs",
      "production": "./esm/react-jsx-dev-runtime.production.mjs",
      "node": {
        "import": "./esm/react-jsx-dev-runtime.node.mjs",
        "require": "./jsx-dev-runtime.js"
      },
      "default": "./jsx-dev-runtime.js"
    },
    "./jsx-runtime": {
      "development": "./esm/react-jsx-runtime.development.mjs",
      "production": "./esm/react-jsx-runtime.production.mjs",
      "node": {
        "import": "./esm/react-jsx-runtime.node.mjs",
        "require": "./jsx-runtime.js"
      },
      "default": "./jsx-runtime.js"
    },
    "./": "./"
  },
}

Нам потребуются новые пакеты ESM, хотя добавить их с помощью накопительного пакета не составит труда.

  • В пакетах ./esm/react.development.mjs и ./esm/react.production.mjs должно быть проверок process.env.NODE_ENV :

    • условие разрешается во время импорта / сборки через поле exports .

    • process - это API узла, он не имеет смысла в среде браузера и, например, не поддерживается _default_ в webpack 5.

  • ./esm/react.node.mjs оставит чеки process.env.NODE_ENV .
  • AFAIK только webpack 5 и node поддерживают поле exports прямо сейчас.

Думаю, это можно добавить, WDYT?

https://webpack.js.org/guides/package-exports/
https://nodejs.org/dist/latest-v15.x/docs/api/packages.html

"./": "./" делает это безопасным, да, но также предотвращает любую инкапсуляцию, поэтому вы захотите удалить это, как только у вас будет semver-major.

FWIW babel выводит новый импорт среды выполнения jsx как

import { jsxs, jsx, Fragment } from 'react/jsx-runtime';

но если вы загрузите такой модуль в Node, он будет жаловаться на отсутствие расширения файла:

> node .\node.mjs
node:internal/process/esm_loader:74
    internalBinding('errors').triggerUncaughtException(
                              ^

Error [ERR_MODULE_NOT_FOUND]: Cannot find module 'test\node_modules\react\jsx-runtime' imported from test\node_modules\react-data-grid\lib\bundle.js
Did you mean to import react/jsx-runtime.js?
    at new NodeError (node:internal/errors:259:15)
    at finalizeResolution (node:internal/modules/esm/resolve:307:11)
    at moduleResolve (node:internal/modules/esm/resolve:742:10)
    at Loader.defaultResolve [as _resolve] (node:internal/modules/esm/resolve:853:11)
    at Loader.resolve (node:internal/modules/esm/loader:85:40)
    at Loader.getModuleJob (node:internal/modules/esm/loader:229:28)
    at ModuleWrap.<anonymous> (node:internal/modules/esm/module_job:51:40)
    at link (node:internal/modules/esm/module_job:50:36) {
  code: 'ERR_MODULE_NOT_FOUND'
}

Добавление exports должно исправить это 🤔

@nstepien, предоставляющий полную карту exports как вы показали в своем предыдущем посте, не является вариантом из того, что я считаю. То, что node реализует в отношении взаимодействия cjs и прочего, не очень хорошо сочетается с существующей экосистемой. Опасность двойного пакета реальна - особенно для таких пакетов, как Reac, для которых требуется их единственная копия.

Карта exports с файлами только commonjs потенциально может быть добавлена, ничего не нарушая, но также должна быть сделана очень осторожно и с соответствующими тестами e2e для этого (учитывая, насколько сложны все для правильного решения)

@Andarist работает нормально и ничем не отличается в случае реакции, который всегда имел эту опасность, и экосистема решает ее, заставляя реагировать равноправным отделом повсюду. Карта «экспорта» может отлично работать здесь, если файлы ESM и файлы CJS имеют одно и то же состояние, что может быть достигнуто путем написания простых оболочек ESM.

Если все зависимости, транзитивные или нет, React находятся под контролем React (что в данном случае так и есть), а точки входа ESM всех из них просто реэкспортируют контент CJS, тогда да - возможно, это достижимо в данном конкретном случае.

Тем не менее, все еще существует драма о том, какой должна быть фактическая форма записи ESM (с именем, по умолчанию, обеими):

  • только именованные: не совсем обратно совместимы, потому что большая часть кода там использует import React from 'react' , что также является единственным способом фактически импортировать React в узел прямо сейчас при использовании ESM
  • только по умолчанию: не имеет обратной совместимости, потому что большая часть нашего кода использует import * as React from 'react' , это часто продвигается с помощью проверки типов и других инструментов
  • оба: единственный способ сделать его полностью обратно совместимым, чтобы он мог работать со всеми текущими стилями загрузки и при смешивании модулей ESM и CJS в дереве зависимостей.

Я постоянно забываю о возможности использования оболочек ESM, поскольку они кажутся читерскими, а также потому, что этот метод работает только в том случае, если вы контролируете все свои зависимости, и его нельзя использовать в качестве универсальной стратегии, которая «просто работала бы» 😢

Боюсь, что единственная универсальная стратегия, обеспечивающая и то и другое, - это, по сути, иметь весь ваш реальный код в CJS и писать вокруг него оболочки ESM для предоставления именованных экспортов. Код, который не отслеживает состояние и не полагается на идентичность, может быть изначально написан на обоих языках, но это подмножество, предостережение.

jsx-runtime не поддерживает состояние, верно? Должно быть безопасно отправлять оба esm / cjs без обертки.

Должен ли я регистрировать отдельную проблему для импорта react/jsx-runtime в узел esm?

Для интерфейсных пакетов к опасности двойного состояния присоединяется дополнительная проблема: назовем ее опасностью двойного пакета.

При экспорте версий ESM и CJS и использовании пакета через require и import обе версии будут объединены и удвоят эффективный размер пакета для пакета.

@sokra возможно ли это на практике? Например, с накоплением я бы предположил, что, поскольку модули cjs преобразуются в модули esm, он предпочел бы импортировать модули esm, когда они доступны.

Это возможно на практике для сборщиков пакетов, следующих семантике узла - например, webpack 5. Важно согласовать семантику, потому что в противном случае u потенциально может получить разные результаты при запуске в узле и при запуске кода пакетов.

Однако webpack 5 обрабатывает специальное условие "module" , которое используется для дедупликации esm / cjs (тех, которые поступают из карты экспорта).

Если мы рассмотрим предложение @ljharb, это не совсем актуально, потому что он предполагает, что файл esm может быть всего лишь несколькими строками кода оболочки, который просто повторно экспортирует файл cjs.

С тонкой оболочкой ESM нет дополнительной опасности, с упаковщиками или без них - только обычная, которую одноранговые департаменты избегают из-за дублирования на графике.

Была ли эта страница полезной?
0 / 5 - 0 рейтинги