React: Formalisierung von ES-Exporten auf höchster Ebene

Erstellt am 9. Nov. 2017  ·  104Kommentare  ·  Quelle: facebook/react

Derzeit versenden wir nur CommonJS-Versionen aller Pakete. Möglicherweise möchten wir sie jedoch in Zukunft als ESM versenden (https://github.com/facebook/react/issues/10021).

Wir können dies nicht ganz einfach tun, weil wir noch nicht wirklich entschieden haben, wie hochrangige ES-Exporte von jedem Paket aussehen würden. Hat beispielsweise react eine Reihe von benannten Exporten, aber auch einen Standardexport namens React ? Sollten wir die Leute ermutigen, import * für ein besseres Baumschütteln zu machen? Was ist mit react-test-renderer/shallow , das derzeit eine Klasse exportiert (und daher in Node fehlschlagen würde, wenn es in einen Standardexport umgewandelt würde)?

Build Infrastructure React Core Team Breaking Change Discussion

Hilfreichster Kommentar

Es ist jetzt fast 2020, ich würde gerne wissen, ob es Updates vom offiziellen FB-Team gibt? Würde es diesbezüglich Änderungen in React v17 geben?

Alle 104 Kommentare

Imho import * ist ein Weg zu gehen, ich bin nicht dagegen, auch einen Standardexport zu haben, aber er sollte nicht verwendet werden, um andere Dinge wie in diesem Beispiel erneut zu exportieren:

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

aber es sollte nicht verwendet werden, um andere Dinge wie in diesem Beispiel zu reexportieren:

Gibt es einen technischen Grund dafür? (Abgesehen davon, dass es zwei Möglichkeiten gibt, dasselbe zu tun.)

Mein Eindruck ist, dass Leute, die * importieren (und nicht den Standard verwenden), keine Probleme mit dem Baumschütteln haben, da der Standard nicht verwendet wird. Aber vielleicht überschätze ich Rollup etc.

Diese Fragen können wahrscheinlich am besten von @lukastaegert beantwortet werden. Ich bin mir nicht sicher, ob sich seit https://github.com/facebook/react/issues/10021#issuecomment -335128611 etwas geändert hat

Außerdem ist Rollup nicht der einzige Tree-Shaker da draußen, und obwohl der Tree-Shaking-Algorithmus von Webpack schlechter ist als der von Rollup, ist seine Nutzung wahrscheinlich viel höher als der von Rollup (beide Tools machen hervorragende Arbeit, ich möchte niemanden beleidigen , nur Fakten nennen) und wenn wir (als Community) beiden Tools gleichzeitig helfen können, sollten wir dies tun, wann immer wir können.

wird Tree-Shaking im Fall von React alles _tun_, da alles zu einem einzigen flachen Bündel vorverarbeitet wird? Ich frage mich, was der primäre Importstil für React ist, persönlich behandle ich ihn wie einen Standardexport, z. B. React.Component , React.Children aber gelegentlich mache ich das genannte Ding mit cloneElement

Wie @gaearon bereits an anderer Stelle erwähnt hat, werden die Größenverbesserungen im Falle einer Reaktion voraussichtlich minimal sein. Dennoch gibt es Vorteile:

  • React.Children wird möglicherweise in einigen Fällen entfernt (das habe ich gehört 😉)
  • React selbst kann durch Modulbundler, die dies unterstützen, in den Spitzenbereich gehoben werden. Dies könnte wieder einige Bytes entfernen und möglicherweise auch eine ach so leichte Leistungssteigerung bescheren. Die Hauptverbesserung würde darin liegen, dass es nicht für jedes Modul eine andere Variable geben muss, die auf React.Component verweist, sondern nur eine, die überall geteilt wird (so macht es Rollup normalerweise). Auch wenn dies nur meine Vermutung ist, könnte dies die Wahrscheinlichkeit verringern, dass das ModuleConcatenationPlugin von Webpack ausfällt
  • Die statische Analyse für React ist nicht nur für Modulbundler, sondern auch für zB IDEs und andere Tools einfacher. Bei CJS-Modulen leisten viele solcher Tools bereits einen vernünftigen Job, aber am Ende ist viel Raten auf ihrer Seite erforderlich. Mit ES6-Modulen ist die Analyse ein Kinderspiel.

Was die Art der Exporte angeht, bieten natürlich nur benannte Exporte wirklich den Vorteil des einfachen Baumschüttelns (es sei denn, Sie verwenden GCC, das in seiner aggressiven Bewegung möglicherweise etwas mehr tun kann und vielleicht das neueste Rollup, wenn Sie wirklich Glück haben). . Die Frage, ob Sie auch einen Standardexport bereitstellen, ist schwieriger zu entscheiden:

  • PRO: Problemlose Migration für bestehende ES6-Codebasen (zB was @jquense beschreibt)
  • CON: Da alles an ein gemeinsames Objekt angehängt ist, werden, sobald dieses Objekt eingeschlossen ist, alle seine Schlüssel auf einmal eingeschlossen, was wiederum alle Versuche des Tree-Shaking verhindert. Selbst GCC könnte es hier schwer haben.

Als Migrationsstrategie mit zwei Versionen können Sie aus Kompatibilitätsgründen in der nächsten Version einen Standardexport hinzufügen, der als veraltet erklärt wird (es könnte sogar eine Warnung über einen Getter anzeigen usw.) und ihn dann in einer späteren Version entfernen.

Auch das ist ein interessanter Fall: https://github.com/facebook/react/issues/11526. Während Monkeypatching zum Testen etwas zwielichtig ist, sollten wir uns bewusst sein, dies zu brechen (oder eine Problemumgehung dafür zu haben).

Kam hier über dieses Twitter-Gespräch . Für mich gibt es eine eindeutig richtige Antwort auf diese Frage: React und ReactDOM sollten nur benannte Exporte exportieren. Sie sind keine Objekte , den Zustand enthält, oder dass andere Bibliotheken können mutieren oder befestigen Eigenschaften (# 11526 ungeachtet) - der einzige Grund , warum sie existieren , ist als ein Ort zum ‚Put‘ Component , createElement und so weiter. Mit anderen Worten, Namensräume, die als solche importiert werden sollen.

(Es macht auch Bündelern das Leben leichter, aber das ist weder hier noch dort.)

Dies stellt natürlich eine entscheidende Änderung für Benutzer dar, die derzeit einen Standardimport und eine Standardübertragung verwenden. @lukastaegert hat hier wahrscheinlich die richtige Idee, indem er Accessoren verwendet, um Warnungen zu veralteten

Ich habe jedoch keinen vorgefertigten Vorschlag für #11526. Vielleicht hätte die Auslieferung von ESM aus diesem Grund sowieso auf v17 warten müssen, in diesem Fall müssten Sie sich keine Sorgen über veraltete Warnungen machen.

Die Leute mögen es wirklich

import React, { Component } from 'react'

Daher könnte es schwierig sein, sie davon zu überzeugen, es aufzugeben.

Ich denke, das ist nicht so schlimm, auch wenn es etwas seltsam ist:

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

Zur Verdeutlichung benötigen wir React im Gültigkeitsbereich (in diesem Fall als Namespace), da JSX nach React.createElement() transpiliert. Wir könnten JSX brechen und sagen, dass es stattdessen von der globalen jsx() Funktion abhängt. Dann würden die Importe so aussehen:

import {jsx, Component} from 'react';

Das ist vielleicht okay, aber eine große Veränderung. Dies würde auch bedeuten, dass React UMD-Builds jetzt auch window.jsx .

Warum schlage ich jsx statt createElement ? Nun, createElement ist bereits überladen ( document.createElement ) und obwohl es mit dem React. Qualifikationsmerkmal in Ordnung ist, ist es einfach zu viel, wenn es nicht global beansprucht wird. Tbh, ich bin von keiner dieser Optionen sehr begeistert und denke, dass dies wahrscheinlich der beste Mittelweg wäre:

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

und lassen Sie JSX standardmäßig auf React.createElement transpilieren.

Geständnis: Ich fand es immer etwas seltsam, dass Sie React explizit importieren müssen, um JSX zu verwenden, obwohl Sie diesen Bezeichner nirgendwo verwenden. Vielleicht könnten Transpiler in Zukunft import * as React from 'react' (konfigurierbar für Preact usw.) einfügen, wenn sie auf JSX stoßen, wenn es noch nicht existiert? So müsstest du nur das machen...

import { Component } from 'react';

...und der Namespace-Import würde automatisch erledigt.

In ferner Zukunft vielleicht. Im Moment müssen wir sicherstellen, dass Transpiler mit anderen Modulsystemen (CommonJS oder Globals) funktionieren. Dies konfigurierbar zu machen, ist ebenfalls eine Hürde und spaltet die Community weiter.

Was @Rich-Harris vorgeschlagen hat (einen bestimmten Import einfügen, wenn jsx verwendet wird) wird mit dem Transpiler-Plugin leicht erledigt. Die Community müsste ihre babel-plugin-transform-react-jsx aktualisieren und das war's. Und natürlich würden auch bestehende Setups noch funktionieren, wenn nur eines import * as React from 'react'; zur Datei hinzufügt.

Natürlich müssen wir andere Modulsysteme in Betracht ziehen, aber es scheint kein schwer zu lösendes Problem zu sein. Gibt es bestimmte Fallstricke im Hinterkopf?

Natürlich müssen wir andere Modulsysteme in Betracht ziehen, aber es scheint kein schwer zu lösendes Problem zu sein. Gibt es bestimmte Fallstricke im Hinterkopf?

Ich weiß nicht, was ist Ihr konkreter Vorschlag, wie man damit umgeht? Wäre die Standardeinstellung für das Babel JSX-Plugin?

Die Leute mögen es wirklich

import React, { Component } from 'react'

Welche Leute? Komm heraus, damit ich dich verspotte.

Das habe ich oft gemacht 🙂 Ziemlich sicher habe ich das auch an anderen Orten gesehen.

Der Standardwert ist im Moment React.createElement und würde so ziemlich gleich bleiben. Das einzige Problem ist, dass es jetzt von einem globalen ausgeht (oder bereits im Umfang verfügbar ist).

Ich denke, da es Module im Grunde genommen die Standardmethode sind (wenn auch noch nicht von allen angenommen), um Module zu machen, ist es vernünftig anzunehmen, dass die Mehrheit es verwenden wird (oder sollte). Die überwiegende Mehrheit verwendet bereits verschiedene Build-Step-Tools, um ihre Bundles zu erstellen – was in dieser Diskussion noch mehr zutrifft, da wir über das Transpilieren der jsx-Syntax sprechen. Das Standardverhalten des jsx-Plugins auf das automatische Einfügen von React.createElement in den Geltungsbereich zu ändern, ist imho vernünftig. Mit babel@7 kommt bald (-ish) der perfekte Zeitpunkt für diese Veränderung. Durch das kürzliche Hinzufügen von babel-helper-module-imports es auch einfacher denn je, den richtigen Importtyp (es/cjs) in die Datei einzufügen.

Dies konfigurierbar zu haben, um dem heutigen Verhalten gerecht zu werden (vorausgesetzt im Umfang vorhanden), scheint wirklich eine kleine Änderung in der Konfiguration zu sein, die für eine Minderheit von Benutzern erforderlich ist, und eine Verbesserung (sicher, keine große - aber immer noch) für die Mehrheit.

Sollten wir die Leute ermutigen, * für ein besseres Baumschütteln zu importieren?

Dank @alexlamsl hat uglify-es die export default Strafe in üblichen Szenarien beseitigt:

$ 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);

wow, das ist eine tolle neue gilt uglify-es jetzt als stabil? Ich erinnere mich, dass Sie vor einigen Monaten erwähnt haben, dass es noch nicht ganz da ist, aber ich kann mich falsch daran erinnern, also bin ich mir nicht sicher.

Wie auch immer - das ist alles in einer Rollup-Welt, aber wenn man bedenkt, dass React hauptsächlich in Apps gebündelt ist und diese hauptsächlich webpack die standardmäßig kein Scope-Heben durchführen, würde ich das trotzdem sagen Das Exportieren eines Objekts als Standard sollte vermieden werden, um andere Tools als uglisy-es + rollup bei ihren Bemühungen zu unterstützen, kleinere Paketgrößen zu erzeugen. Auch für mich ist es semantisch besser, dies zu vermeiden - was Libs in solchen Fällen tatsächlich tun, ist einen Namensraum bereitzustellen und dieser wird besser dargestellt, wenn import * as Namespace from 'namespace'

gilt uglify-es jetzt als stabil?

So stabil wie alles andere im JS-Ökosystem. Über 500.000 Downloads pro Woche.

das ist alles schön in einer Rollup-Welt, aber wenn man bedenkt, dass React hauptsächlich in Apps gebündelt ist und diese hauptsächlich Webpack verwenden, das standardmäßig kein Scope-Hoisting durchführt

Jedenfalls ist es eine Option. Webpack-Standardeinstellungen sind sowieso nicht ideal - Sie müssen ModuleConcatenationPlugin wie Sie wissen.

Fügen Sie hier ein paar Cent hinzu:

  • Ich stimme @Rich-Harris voll und ganz zu, dass benannte Exporte semantisch die richtige Wahl sind
  • Ich mag weder import React from 'react' noch import * as React from 'react' nur um die JSX-Syntax verwenden zu können. In meinen Augen verstößt dieses Design eindeutig gegen das Interface-Segregation-Prinzip, da es die Benutzer zwingt, das gesamte React zu importieren, nur um den createElement Teil verwenden zu können (obwohl zugegebenermaßen bei einem Namespace-Export ein Bundler wie Rollup dies tun wird). Entfernen Sie die nicht benötigten Exporte wieder)

Wenn wir also an einem Punkt angelangt sind, an dem wir möglicherweise Breaking-Change-Entscheidungen treffen, würde ich empfehlen, dies so zu ändern, dass JSX von einer einzigen (globalen oder importierten) Funktion abhängt. Ich hätte es createJSXElement() , was es meiner Meinung nach sogar besser als createElement() und den React-Kontext nicht mehr benötigt, um Sinn zu machen. Aber in einer Welt, in der jedes Byte zählt, ist jsx() wahrscheinlich auch in Ordnung.

Dies würde auch JSX endlich so von React entkoppeln, dass andere Bibliotheken JSX unterstützen können, indem sie dieselbe Transformation verwenden und eine andere jsx Funktion bereitstellen. Natürlich trägt man hier eine große Verantwortung, unzählige etablierte Anwendungen durch eine solche Transformation zu führen, aber aus architektonischer Sicht denke ich, dass React und JSX hier hingehen sollten. Babel für die schwere Arbeit einer solchen Transformation zu verwenden, klingt für mich nach einer großartigen Idee!

Persönlich sehe ich keinen großen Vorteil in der Migration auf den jsx Helfer, da der Standard-IMHO für das Babel-Plugin es aus dem react Paket importieren sollte, also der Name des tatsächlichen Helfers nicht wirklich egal - der Rest ist einfach konfigurierbar.

Dies ist wahrscheinlich etwas tangential zur Hauptdiskussion, aber ich bin gespannt, wie gut ES-Module mit der Überprüfung von process.env.NODE_ENV funktionieren, um bedingte Dev/Prod-Bundles zu exportieren? Zum Beispiel,

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

Vielleicht übersehe ich hier etwas Offensichtliches, aber ich habe Schwierigkeiten zu sehen, wie ich dieses Muster in ES-Module übersetzen kann?

@NMinhNguyen Bedingte Exporte sind mit ES-Modulen nicht möglich.

process.env.NODE_ENV Prüfungen können jedoch auf einer detaillierteren (Code-)Ebene erfolgen und können vom Bundler mit entsprechenden Werten ersetzt werden.

@Andarist @milesj Danke für die Bestätigung meines Verdachts :)

process.env.NODE_ENV Prüfungen können jedoch auf einer detaillierteren (Code-)Ebene erfolgen und können vom Bundler mit entsprechenden Werten ersetzt werden.

Aus dem React 16- Blogbeitrag dachte ich, dass die process.env.NODE_ENV Schecks absichtlich ganz nach oben gezogen wurden (im Gegensatz dazu, dass sie detaillierter sind, was sie in der Quelle sind, wenn ich mich nicht irre) ), um die Leistung in Node.js zu verbessern?

Besseres serverseitiges Rendering

React 16 enthält einen komplett neu geschriebenen Server-Renderer. Es ist wirklich schnell. Es unterstützt Streaming , sodass Sie schneller Bytes an den Client senden können. Und dank einer neuen Paketierungsstrategie , die process.env Checks wegkompiliert (ob Sie es glauben oder nicht, das Lesen von process.env in Node ist wirklich langsam!), müssen Sie React nicht mehr bündeln, um gute Server- Rendering-Leistung.

Ich bin mir zum Beispiel nicht sicher, wie man das Feld module in package.json und zwischen dev/prod für ESM unterscheiden kann, während die ES-Bundles flach gehalten werden und die Leistung von Node.js nicht beeinträchtigt wird

Ich bin mir nicht sicher, wie man das Modulfeld in package.json verwenden und zwischen dev/prod für ESM unterscheiden kann, während ES-Bundles flach gehalten werden und die Leistung von Node.js nicht beeinträchtigt wird

Dies ist sicherlich ein Nachteil, da es derzeit keine Standardmethode dafür gibt. OTOH es ist nur eine Frage des Werkzeugs, es ist möglich (und es ist ziemlich einfach), dies auch heute noch in Build-Schritten Ihrer Anwendung zu kompilieren. Oft wäre es einfacher, wenn das Paket dev/prod-Builds verfügbar machen könnte und der Resolver nur wüsste, welchen er auswählen soll, aber vielleicht geht es nur darum, diese Idee an die Tool-Autoren weiterzugeben.

Für Klasse:

import Component from 'react/Component'

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

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

Wobei transform super.createElement() verwendet, um in jsx umzuwandeln, oder statisches Component.createElement() verwendet.

Für zustandslose Komponenten:

import jsx from 'react/jsx'

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

ist es vielleicht möglich, mit Tags versehenes Vorlagenliteral zu verwenden?

Node akzeptiert hoffentlich diese PR https://github.com/nodejs/node/pull/18392

Wir stimmen hier @Rich-Harris zu.

Schreibe einfach einen Kommentar zu diesem Thread, der nicht wirklich speziell erwähnt wurde.

Ich befinde mich in einer Situation, in der ich überhaupt keinen Bundler verwende und nur React und verschiedene Komponenten zur nativen Verwendung über den Browser importieren möchte ( <script type="module" src="..."> ), dh

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')
);

Soweit ich das beurteilen kann, ist dies heute nicht möglich. Stattdessen muss ich die UMD-Version von React über ein <script> Tag aus dem CDN einbinden und dann davon ausgehen, dass es im Fenster in jedem <script type="module"> Modul vorhanden ist, das ich schreibe:

// 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 {...}

Ein React-Import von einem CDN wäre fantastisch. Es würde das Prototyping im Browser sehr schnell und einfach machen und trotzdem in der Lage sein, die Trennung der Dateien aufrechtzuerhalten. Eine Sache, die ich immer opferte, wenn ich React ohne Bundler verwende, war die Möglichkeit, Komponenten (und andere Dienstprogrammfunktionen usw.) nach Datei zu trennen. Aber jetzt mit Browser-Unterstützung für native ES-Module kann ich meine React-Komponenten in separate Dateien schreiben und den Browser so verwenden, wie sie geschrieben wurden. Zugegeben, wenn ich JSX nicht verwende, aber selbst wenn ich JSX verwende, könnte ich alle Dateien über einen Build-Schritt transpirieren und alle meine Importe würden immer noch im Browser funktionieren.

// /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, ...)
}

Ich bin sicher, einige Leute würden argumentieren, dass es ein Problem ist, die CDN-URL die ganze Zeit einzugeben (ein Problem, das einige Leute zu beheben versuchen), aber die Kompromisse sind es mir wert. Das Ändern/Aktualisieren dieser URL ist ein einfaches Suchen/Ersetzen. Für meinen Anwendungsfall überwiegt dies die Mühe, einen Bundler einzurichten.

Wenn React für so etwas Unterstützung hätte, wären keine Tools erforderlich. Ich benutze nur den Browser. Ich könnte Code wie diesen in ein paar persönlichen Projekten liefern, die moderne Browser voraussetzen und als progressive Verbesserung auf der Seite reagieren. Was dies fantastisch macht, ist, dass ich, wenn ich in 12 Monaten zur Codebasis zurückkomme, keine Reihe von Tooling-APIs ändern oder sogar NPM als Paketmanager verwenden muss. Ich verwende nur APIs aus dem Browser, sonst nichts.

FWIW: Wenn/wenn React mit einer solchen Unterstützung ausgeliefert wird, könnte es meiner Meinung nach sehr wertvoll sein, in den Dokumenten zu zeigen, wie Sie React so verwenden können , indem Sie lehren, dass Sie React und sein Komponentenmodell nutzen können, indem Sie die Logik der einzelnen Komponenten durch ihre trennen eigene Datei und Sie brauchen dafür keinen Bundler, verwenden Sie einfach natives <script type="module"> , importieren Sie React von einem CDN (oder Ihrer eigenen lokalen Kopie) und los geht's!“

Jetzt unterstützen alle modernen Browser, einschließlich mobiler Versionen, ESM. ESM ist kein zukünftiges Modulsystem mehr, sondern ein aktueller Defacto-Standard.

Bitte beachten Sie, dass die Nichtbereitstellung des standardisierten Moduls ein kritisches Problem darstellt, insbesondere bei einer defacto-standardisierten Webbibliothek.

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

Dies ist der typische Code zum Anwenden von React-Bibliotheken, und Tatsache war, dass es keine wirklichen Bibliotheken gibt, die importiert werden können, stattdessen emulieren Transpiler und Bundler von Drittanbietern den Importprozess.

Es war ein wenig gerechtfertigt, das echte ESM nicht bereitzustellen, da Browser das native ESM sowieso nicht unterstützt hatten, aber offensichtlich ist die Zeit abgelaufen, und jetzt ist es an der Zeit, ESM wie angegeben den typischen Beispielcode für import bereitzustellen.

Ich habe hier und hier angefangen, daran zu arbeiten

@TrySound Danke für Ihren Beitrag.
Gibt es einen Ort, an dem Sie den ESM-Build abrufen und testen können?

Es ist nur für das Reaktionspaket bereit.

@TrySound
Ok, ich habe deinen Zweig https://github.com/TrySound/react/tree/react-is-esm gefunden und gebaut, und jetzt weiß ich, was du meinst. Freue mich auch auf react-dom .

Ich denke, die React-Community hat dieses Thema eine Weile genug diskutiert.
https://discuss.reactjs.org/t/es6-import-as-react-vs-import-react/360/

Bitte entscheiden Sie sich für die offizielle ES6-Modulspezifikation und veröffentlichen Sie sie in Kürze.

@kenokabe Wir sind unterwegs. Zwingen Sie uns bitte nicht. Es ist nicht so leicht.

Der aktuelle Plan sieht vor, alle Pakete mit nur benannten Exporten zu migrieren. Diese Änderung wirkt sich nicht auf den Bibliothekscode aus und sollte keine grundlegenden Änderungen einführen, da Docs auch benannte Exporte verwendet.

Für andere Pakete müssen wir sowohl Standard- als auch benannte Exporte verarbeiten, die mit verschiedenen Tools unterschiedlich funktionieren.

@TrySound Ich entschuldige
Ich meinte nicht zu dir, da die Haupterwähnung dieses Themas ist

Wir können dies nicht ganz einfach tun, weil wir noch nicht wirklich entschieden haben, wie hochrangige ES-Exporte von jedem Paket aussehen würden. Hat beispielsweise React eine Reihe von benannten Exporten, aber auch einen Standardexport namens React? Sollten wir die Leute ermutigen, * für ein besseres Baumschütteln zu importieren?

und der erwähnte Tag ist schon eine Weile her und ich dachte nur, dass es in der React-Community diskutiert wurde, also wollte ich vorschlagen, dass die Entscheidung klar ist. Vielen Dank!

Willst du ein Update dazu bekommen...

Ich verwende Webpack v4 zum Bündeln unserer Anwendung, während meine IDE intellisense (WebStorm) mir vorschlägt, import * as React from 'react'; während mein Kollege mich bittet, import React from 'react'; in einer Codeüberprüfung zu ändern. Beides funktioniert gut, also dachte ich, er sagt etwas Unsinn, aber um ihn glücklich zu machen, ändere ich es trotzdem. So finde ich diesen Thread auch.

Aus Neugier vergleiche ich die Unterschiede bei der endgültigen Build-Größe (mit React 16.8.1):

In import * as React from 'react'; : 6.618.723 Byte
In import React from 'react'; : 6.619.077 Byte

Es gab also offensichtlich einige Unterschiede, wenn auch marginal. (Anmerkung. Ich habe das gleiche mit propTypes )

Wenn ich diesen Thread richtig verstehe, wäre es für import * as React from 'react'; günstig, oder?! Denn (1) ja, es hat etwas an Größe gespart; (2) ESM ist ein standardisierter Weg, sodass keine CJS-Reste mehr übrig bleiben. Wenn dies der Fall ist, möchte ich dies heute ändern und an meiner IDE ausrichten.

@leoyli Langfristig ja. Aber zuerst wird es sowohl benannte als auch standardmäßige Exporte geben, um vorhandenen Code nicht zu beschädigen.

Ich habe die Sache hier selbst in die Hand genommen, eher als Experiment, da ich in meinen Projekten keinen Bundler mehr verwende und trotzdem reagieren wollte (direkt von unpkg.com wie bei anderen Bibliotheken wie Vue, Hyperapp usw.) . Das ist, was ich mir ausgedacht habe, nichts Besonderes, nur ein handbearbeitetes umd:

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

Ein ES6-Modul, das die neueste Version von React und ReactDOM

Wie in der README beschrieben, ist dies hauptsächlich ein POC, aber für Leute, die nicht warten können, bis dieser Build landet, ist es ein 16.8.3 Build, der Hooks, Spannung, Lazy usw. enthält und wie erwartet funktioniert:

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

Vielleicht finden es einige von Ihnen in diesem Thread nützlich. Persönlich habe ich diesen Ansatz verwendet, um ein Build Step Free Reaction Starter-Projekt zu erstellen. Es ist auch noch in Arbeit.

@lukejacksonn Wir haben eine solche Lösung auch in der Produktion verwendet, während unser Ansatz https://github.com/wearespindle/react-ecmascript und Sie können es auch von unpkg https://unpkg.com/react-ecmascript/ laden

@PM5544 Oh wow.. das ist eine viel umfassendere Lösung als meine! Tolle Arbeit 💯

Tolles Zeug @ PM5544. Würde gerne eines Tages mehr darüber hören. Vielleicht ein Gastspiel bei Xebia?
Ich habe vor kurzem Pack zum Bündeln meiner Open-Source-Pakete eingeführt, das UNPKG unterstützt.
Kennt jemand einen guten Artikel zum direkten Laden von Abhängigkeiten von UNPKG, anstatt einen Bundler zu verwenden?

Ich schreibe gerade einen und werde ihn im Juni auch als Vortrag bei React Norway halten!

@TrySound Gibt es dazu seit Februar ein Update? Was bleibt noch übrig, um dieses Problem in Gang zu bringen, und kann ich dazu beitragen, dass dieses Problem durch einige Codierungsarbeiten geschlossen wird? Ich habe die CA bereits unterschrieben und habe heute Zeit, daran zu arbeiten.

Dies muss zuerst zusammengeführt werden https://github.com/facebook/react/pull/15037

@TrySound Okay, danke, ich habe mein Hilfeangebot an diesen Thread weitergeleitet.

Wenn Sie einen standardmäßigen React-Export verwenden, können Sie diesen Ansatz verfolgen:

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

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

Dadurch ist es statisch analysierbar, dass der Standardexport ein Namespace-Objekt ist, das das Tree-Shaking für diese Konstrukte in Webpack 5 und Rollup ermöglicht:

import React from "react";

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

Ich bin seit 1,5 Jahren im Rollup-Gitter-Chat und solche Probleme tauchen alle 2 Wochen oder so auf ...

Übrigens , Es- React - Fork hervorragende Arbeit geleistet, kann es wärmstens empfehlen.

Übrigens , Es- React - Fork hervorragende Arbeit geleistet, kann es wärmstens empfehlen.

Ich frage mich, warum das offizielle FB-Team nichts dagegen tut.

Ich hatte auch gehofft, dass dies einen gewissen Druck erzeugen würde, die Dinge voranzutreiben, und ich denke, @lukejacksonn selbst war es auch. Wie ich jedoch verstanden habe, hat er tatsächlich etwas Unterstützung vom React-Team erhalten, um seinen Fork aus den Originalquellen zu bauen, also scheint es zumindest ein gewisses Interesse daran zu geben.

Das habe ich auch gehofft. Tatsächlich habe ich bei der Erstellung des Pakets so gut wie keine Unterstützung vom Reaktionsteam erhalten (außer ein paar aufmunternden Worten von @threepointone). Kürzlich hat mir ein Kollege hier bei Formidable geholfen, das Paket programmatisch zu erstellen, was eine Verbesserung gegenüber der manuellen Ausführung ist, wenn eine neue Version von React veröffentlicht wird, aber zu einer viel weniger sauberen Ausgabe auf der Registerkarte "Netzwerk" führt, daher bin ich mir nicht sicher, ob dies der Fall ist bleib noch so. Wir werden sehen!

Es ist jetzt fast 2020, ich würde gerne wissen, ob es Updates vom offiziellen FB-Team gibt? Würde es diesbezüglich Änderungen in React v17 geben?

Für diejenigen, die ein aktualisiertes ES-Modul benötigen, JETZT reagieren, versuchen Sie @pica/react , das bereits auf v16.13.x verfügbar ist
https://www.npmjs.com/package/@pika/react
https://www.npmjs.com/package/@pika/react -dom
https://github.com/pikapkg/react

In ferner Zukunft vielleicht.

Okay, schon in der Zukunft. Ist es jetzt in naher Zukunft?

@gaearon was ist der Blocker für die Entscheidung, wie die Exporte strukturiert werden sollen (Standardexport vs. benannte Exporte)? Kann Ihnen die Community bei dieser Entscheidung helfen?

Offenbar ist diese Entscheidung schon vor einiger Zeit gefallen: #18102. Dieses Problem kann jetzt geschlossen werden.

Dazu gebe ich ein kleines Update.

Keine Standardexporte

Unser endgültiger Plan ist es, vollständig von Standardexporten abzurücken:

import { useState } from 'react';

In dieser Welt würde das nicht funktionieren:

import React from 'react'; // no

Dies würde funktionieren, obwohl es ein bisschen laut ist:

import * as React from 'react';

Aber hier ist der Kicker. Es gäbe eigentlich keinen Grund, React überhaupt zu importieren.

Automatischer JSX-Import

Der Grund, warum Leute heute React importieren, liegt hauptsächlich an JSX. Aber @lunaruan beendet die Arbeit an der neuen JSX-Transformation und den zugehörigen Codemods, die die Notwendigkeit dafür beseitigen.

Du würdest also davon ausgehen:

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

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

dazu:

import { useState } from 'react';

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

JSX fügt den korrekten Import automatisch unter der Haube ein, sodass React im Umfang nicht erforderlich sind.
Dies macht den Schritt zum Entfernen von Standardexporten tolerierbar. Sie brauchen sie nur nicht so sehr.

ES-Module

Die Bereitstellung von ESM über eine insgesamt kleine Gruppe von Enthusiasten hinaus ist eine Herausforderung. Das breite Ökosystem ist noch nicht wirklich fertig und es gibt eine Menge Möglichkeiten, wie mit verschiedenen Werkzeugkombinationen etwas schief geht. Die Art und Weise, wie CJS und ESM interagieren, ist sehr komplex, und diese Interoperabilität (und wie sie fehlschlägt) ist die Ursache für die meisten dieser Probleme.

Daher denken wir derzeit, dass wir, wenn wir auf ESM setzen, vielleicht versuchen möchten, vollständig auf ESM zu setzen. Überhaupt kein CJS – oder getrennt in einem kompatiblen Legacy-Paket. Dies wird in React 17 nicht passieren und ist in 18 unwahrscheinlich, aber es ist plausibel, es in React 19 zu versuchen.

Für alle, die nach einer Alternative zum ESM-Build von @pika/react suchen, besuchen Sie https://github.com/esm-bundle/react und https://github.com/esm-bundle/react-dom. Der Unterschied besteht darin, dass diese in Browsern ohne ein Import-Map-Polyfill verwendet werden können - das import React from 'react'; im Quellcode von React-Dom wird geändert, um React von einer vollständigen CDN-URL zu importieren. Ein weiterer Unterschied besteht darin, dass neue Versionen automatisch veröffentlicht werden, wenn eine neue Reaktionsversion veröffentlicht wird, ohne dass manuelle Schritte erforderlich sind.

Code-Sandbox-Demonstration: https://codesandbox.io/s/gifted-roentgen-qcqoj?file=/index.html

Einige Leute haben die Vorstellung in Frage gestellt, dass das Ökosystem noch nicht bereit ist. Ich habe nicht den vollständigen Kontext dazu, aber wenn Sie der Meinung sind, dass dies ein guter Zeitpunkt ist, um mit Änderungen zu beginnen, würde ich mich freuen, wenn Sie https://github.com/reactjs/rfcs/pull/38 nachschauen und alle äußern könnten Bedenken darüber. Hätten Sie das in etwa im Sinn oder bevorzugen Sie eine andere Herangehensweise?

Aber hier ist der Kicker. Es gäbe eigentlich keinen Grund, React zu importieren.

Dies ist der Fall, wenn Sie TypeScript verwenden und dies auf absehbare Zeit auch weiterhin tun werden. Bis TS von diesem neuen magischen Verhalten von Babel erfährt, müssen Entwickler React weiterhin explizit importieren, und sie müssen wissen, was die richtige Importanweisung ist.

JSX fügt den korrekten Import automatisch unter der Haube ein, sodass React im Umfang nicht erforderlich ist.

s/JSX/Das neue Babel-React-JSX-Transformations-Plugin/. JSX ist eine Syntaxerweiterung für JavaScript, die selbst nichts tut.

Dies ist der Fall, wenn Sie TypeScript verwenden und dies auf absehbare Zeit auch weiterhin tun werden. Bis TS von diesem neuen magischen Verhalten von Babel erfährt, müssen Entwickler React weiterhin explizit importieren, und sie müssen wissen, was die richtige Importanweisung ist.

Das TypeScript-Team ist sich dieser Änderung bewusst und wird in https://github.com/microsoft/TypeScript/issues/34547 verfolgt. Wir stehen in engem Kontakt mit ihnen, seien Sie also versichert, dass dies für uns kein Bürger zweiter Klasse ist.

s/JSX/Das neue Babel Reagieren jsx-Transformations-Plugin/

Ja, das meinte ich!

Ich würde mich freuen, wenn Sie sich Reactjs/rfcs#38 ansehen und Bedenken äußern könnten. Hätten Sie das in etwa im Sinn oder bevorzugen Sie eine andere Herangehensweise?

Einige der Originaltexte des RFC sind veraltet. NodeJS ermöglicht es, dass ESM jetzt in .js Dateien statt in .mjs , wenn Sie "type" in Ihrer package.json angeben. ( Dokumente ).

Insbesondere sagt der RFC-Originaltext Folgendes, was nicht stimmt:

Der ESM-Code muss sich in einer .mjs Datei befinden

Nach einem Gespräch mit @frehner ist hier unser Vorschlag, wie React schrittweise auf ESM umgestellt werden könnte. Beachten Sie, dass wir das Problem der benannten/standardmäßigen Exporte nicht mit der Veröffentlichung einer ESM-Version von React verbinden. @gaearon hat klargestellt, dass der Standardexport irgendwann

| | Phase 1 | Phase 2 | Phase 3 | Phase 4 |
| - | -------- | -------- | -------- | -------- |
| ESM veröffentlicht? | ✔️ | ✔️ | ✔️ | ✔️ |
| Paket.json "module" | | ✔️ | ✔️ | ✔️ |
| webpack/rollup verwenden esm 1 , 2 | | ✔️ | ✔️ | ✔️ |
| package.json "exports" | | | ✔️ | ✔️ |
| Paket.json "type" | | | ✔️ | ✔️ |
| NodeJS verwendet esm | | | ✔️ | ✔️ |
| Wandel brechen? | | | | ✔️ |
| Standardexport weg? | | | | ✔️ |
| Bei Importen erforderliche Dateierweiterungen | | | | |
| mjs-Dateierweiterungen | | | | |

Ich denke, es gibt ein berechtigtes Argument dafür, Phase 1 und Phase 2 miteinander zu kombinieren, da Phase 1 wirklich nur Enthusiasten anspricht. Ich habe sie jedoch aufgeteilt, weil ich denke, dass die Trennung der Phasen die Möglichkeit bietet, ESM sehr langsam so einzuführen, dass CRA und das gesamte Ökosystem nicht sofort zerstört werden, ohne zuerst den Early Adopters die Möglichkeit zu geben, Probleme zu melden und Korrekturen zu finden .

@joeldenning wie wäre das ungefähre Timing der Phasen? ist es nur zeitbasiert oder hängen diese mit einigen Zeitkontrollpunkten im Ökosystem zusammen? Würde require('react') in Phase 3 noch funktionieren?

Wie wäre das ungefähre Timing der Phasen? ist es nur zeitbasiert oder hängen diese mit einigen Zeitkontrollpunkten im Ökosystem zusammen?

Der Zeitplan für alle Phasen wird nur durch die zu erledigende Arbeit und den Release-Zeitplan von React begrenzt. Nach meinem Verständnis werden alle vorgeschlagenen Dinge von Bundlern und Nodejs unterstützt, mit entsprechenden Fallbacks, wenn ältere Versionen verwendet werden, die sie nicht unterstützen.

Würde require('react') in Phase 3 noch funktionieren?

Ich denke schon. Siehe https://nodejs.org/api/esm.html#esm_dual_commonjs_es_module_packages und https://nodejs.org/api/esm.html#esm_package_entry_points. Es ist natürlich gut möglich, dass ich nicht alle Eckfälle für alles kenne, aber mein Verständnis ist, dass der von mir vorgeschlagene Übergangspfad "überall funktionieren wird". Vielleicht sind das berühmte letzte Worte 😄

Eine zusätzliche Klarstellung: Wir schlagen vor, dass der veröffentlichte Tarball für React aussehen würde:

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

Und hier ist eine Annäherung an das, was die package.json am Ende wäre:

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

^ Dies ist nicht vollständig durchdacht und perfektioniert, aber ich teile es, um dem Vorschlag einen konkreten Kontext zu geben.

Also - React leidet unter der Doppelpaket-Gefahr, weil es zustandsbehaftet ist (Hooks), also müsste dieser Zustand sorgfältig isoliert werden. Entweder sollte es in einer kleinen CJS-Datei leben, die sowohl von CJS- als auch von ESM-Einträgen importiert werden könnte, oder vielleicht gibt es eine Möglichkeit, .json zu laden und es mit diesem Zustand aus beiden Einträgen zu mutieren (ich bin mir da nicht 100% sicher der zweite Ansatz).

Ich denke auch, dass es wichtig ist, in Ihrer Tabelle benannte Exporte hinzuzufügen, was bereits in Phase 1 geschehen würde.

Ich bin mir immer noch nicht 100% sicher, ob ich nicht einige Eckfälle übersehen habe, dieses Thema ist sehr komplex. Gleichzeitig sollte der Zeitplan an die Checkpoints des Ökosystems geknüpft sein – Phase 3 kann erst versendet werden, wenn exports genügend Unterstützung in Bundles hat, sonst könnte dies meiner Meinung nach zu potenziellen Problemen führen.

React leidet unter der Doppelpaketgefahr, da es zustandsbehaftet ist (Hooks), daher müsste dieser Zustand sorgfältig isoliert werden.

Guter Punkt, hatte ich nicht bedacht ( mehr Infos ). In diesem Fall wäre vielleicht Ihr Vorschlag einer gemeinsamen Datei zwischen den CJS / ESM-Builds nützlich. Oder vielleicht ist die ESM-Version keine vollständige Kopie von React, sondern nur die öffentliche ESM-Schnittstelle, die den CJS-Build aufruft.

Ich denke auch, dass es wichtig ist, in Ihrer Tabelle benannte Exporte hinzuzufügen, was bereits in Phase 1 geschehen würde.

Im Quellcode scheint dies jedoch bereits der Fall zu sein? Soweit ich das beurteilen kann, exportiert der Quellcode bereits Dinge als benannte Exporte.

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

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

Ich bin mir immer noch nicht 100% sicher, ob ich nicht einige Eckfälle übersehen habe, dieses Thema ist sehr komplex. Gleichzeitig sollte der Zeitplan an die Ökosystem-Checkpoints geknüpft sein - Phase 3 kann nur versendet werden, wenn der Export ausreichend Unterstützung in Bundles hat, sonst könnte dies meiner Meinung nach zu potenziellen Problemen führen.

Einverstanden bezüglich Phase 3 - deshalb habe ich ein Fragezeichen gesetzt, ob es eine bahnbrechende Veränderung war. Ich weiß, dass das Hinzufügen von package.json-Exporten für andere Pakete im Ökosystem oft eine bahnbrechende Änderung war. Und das Thema ist definitiv komplex. Zu beachten ist, dass die Reihenfolge von Phase 3 und 4 bei Bedarf vertauscht werden kann. Ich denke, dass diejenigen, die jede Phase implementieren, sehr, sehr gründliche Tests vieler Versionen von Webpack, Rollup, Nodejs usw. durchführen müssten. Ich sage nicht, dass die zu erledigende Arbeit trivial ist - ich sage nur, dass ich denke, dass es da ist ist hier wahrscheinlich ein Übergangsweg :)

Im Quellcode scheint dies jedoch bereits der Fall zu sein? Soweit ich das beurteilen kann, exportiert der Quellcode bereits Dinge als benannte Exporte.

Ah - richtig, in diesem Fall sollte es eine Tabellenzeile zum Hinzufügen von export default 😂 geben, da es derzeit in den meisten Bundles funktioniert und in der Wildnis beliebt ist, aber das Hinzufügen eines echten ESM-Eintrags ohne Angabe von default würde brechen Sie diese Verwendungen.

In diesem Fall sollte es eine Tabellenzeile zum Hinzufügen von Exportvorgaben geben

Ja, guter Punkt. Ich werde das hinzufügen. Ich denke, dass ein PR, der das tut, "schlecht erscheinen" würde, aber ich sehe es als das Akzeptieren der aktuellen öffentlichen Schnittstelle als das, was sie ist, mit Plänen, in Zukunft zu verbessern.

Wir sollten beachten, dass es, sobald eine Version veröffentlicht wird, kein Zurück mehr gibt und jede semantische Änderung daran kaputt wäre. Da wir Majors nicht sehr oft veröffentlichen, sollten wir uns überlegen, wie wir Änderungen stapeln, damit die Abwanderung am wenigsten ist.

Es stellt sich auch die Frage, wie die Build-Aufteilung zwischen Entwicklung und Produktion gehandhabt wird. Und tatsächlich ist es sehr wichtig, die zustandsorientierte Natur des React-Builds zu erhalten.

Wir sollten beachten, dass es, sobald eine Version veröffentlicht wird, kein Zurück mehr gibt und jede semantische Änderung daran kaputt wäre. Da wir Majors nicht sehr oft veröffentlichen, sollten wir uns überlegen, wie wir Änderungen stapeln, damit die Abwanderung am wenigsten ist.

Gute Argumente. Meine Idee ist, einem ESM-Build, der in React 16 veröffentlicht wird, ein explizites export default React hinzuzufügen. Ich denke, die PR könnte einige Augenbrauen hochziehen und könnte kontrovers sein, da dies nicht das Ziel ist, für das entschieden wurde. Ich bin jedoch der Meinung, dass wir einen ESM-Build in React 16 haben und dann export default in einer zukünftigen Hauptversion entfernen können. Für mich ist die Verwendung von Reagieren über import React from 'react'; so überwältigend üblich, dass das Exportieren von Standardeinstellungen im ESM-Build einfach "akzeptieren" bedeutet, wo wir sind. Eine zukünftige Hauptversion würde es entfernen.

Auch im Zusammenhang mit der Minimierung von Breaking Changes - Phase 3 und 4 könnten Teil derselben Hauptversion sein. Es ist möglich, dass Phase 3 vollständig abwärtskompatibel ausgeführt wird, in diesem Fall könnte sie sich auch in eine 16er-Version einschleichen. Aber das ist kniffliger und ich weiß nicht genug darüber, um darauf vertrauen zu können.

Es stellt sich auch die Frage, wie die Build-Aufteilung zwischen Entwicklung und Produktion gehandhabt wird.

Das habe ich übersehen. Ich weiß nicht, wie man dies mit ESM sowohl in Bundler als auch in NodeJS macht, aber ich werde einige Nachforschungen anstellen, um zu sehen, was möglich ist. Ich habe diesen toten Vorschlag gefunden , werde aber nach lebendigen suchen :)

Und tatsächlich ist es sehr wichtig, die zustandsorientierte Natur des React-Builds zu erhalten.

Einverstanden. Zu beachten ist, dass die zustandsbehaftete Natur des React-Builds nur in Phase 3 gelöst werden muss, nicht in den Phasen 1 und 2. Die Optionen, die @Andarist und ich vorgeschlagen haben, würden für die Lösung funktionieren.

Die einfachste, rückwärtskompatibel Ansatz für jetzt ist ein einfachen ESM - Wrapper hinzufügen für benannte Importe zu ermöglichen.

Ich würde gerne eine PR erstellen, um dies zu React hinzuzufügen, um sicherzustellen, dass das Hinzufügen des Felds "Exporte" keine grundlegende Änderung ist (ich habe ein Tool geschrieben, npx ls-exports , das die Bestimmung erleichtert). Es wird Leuten nicht helfen, die versuchen, ESM ohne einen Build-Prozess zu verwenden, aber das ist ein Problem, das einzelne Pakete sowieso nicht lösen können.

@gaearon wäre das nützlich? Es könnte als Semver-Minor in React 16 landen.

Der CJS-Build würde weiterhin die Umgebungserkennung verwenden, wie es jetzt der Fall ist, sodass das (harte, ungelöste) Problem in ESM noch nicht gelöst werden muss.

Ich bin jedoch der Meinung, dass wir einen ESM-Build in React 16 haben und dann den Exportstandard in einer zukünftigen Hauptversion entfernen können.

Eine weitere Sorge besteht darin, dass die Zunahme der Anzahl von Konfigurationen das Ökosystem belastet. Zum Beispiel denke ich, wenn wir einen ESM-Build veröffentlichen, sollte er nichts enthalten, was wir definitiv in der nächsten Version entfernen werden, wie zum Beispiel einen Standardexport. Mit anderen Worten, ich denke nicht, dass es sehr sinnvoll ist, etwas, das wegfällt (Standardexport) in etwas einzuführen, das gerade hinzugefügt wird (ESM-Build).

Ich bin jedoch der Meinung, dass wir einen ESM-Build in React 16 haben und dann den Exportstandard in einer zukünftigen Hauptversion entfernen können.

Eine weitere Sorge besteht darin, dass die Zunahme der Anzahl von Konfigurationen das Ökosystem belastet. Zum Beispiel denke ich, wenn wir einen ESM-Build veröffentlichen, sollte er nichts enthalten, was wir definitiv in der nächsten Version entfernen werden, wie zum Beispiel einen Standardexport. Mit anderen Worten, ich denke nicht, dass es sehr sinnvoll ist, etwas, das wegfällt (Standardexport) in etwas einzuführen, das gerade hinzugefügt wird (ESM-Build).

Ich denke, es hängt jedoch davon ab, wie lange die nächste Veröffentlichung dauert. Wenn wir von einer Woche Unterschied sprechen, dann erscheint das vernünftig. Wenn wir jedoch über Monate/Jahre sprechen, verstehe ich nicht, warum es schlecht wäre, es für so lange herauszuholen

Die Sorge in diesem Fall ist, dass je länger es da draußen ist, desto mehr Menschen hängen davon in der aktuellen Form ab. Und die Einführung einer Breaking Change dort würde es ihnen erschweren, React zu aktualisieren. Jetzt gibt es nicht nur eine ESM-Migration, sondern mehrere, und einige Leute werden zurückbleiben, weil sie zu früh gesprungen sind und später nicht die Ressourcen für eine weitere Migration haben. Was sich im Falle von ESM im gesamten Ökosystem ausbreiten wird.

Ich denke, wenn wir einen ESM-Build veröffentlichen, sollte er nichts enthalten, was wir definitiv in der nächsten Version entfernen werden, wie z. B. einen Standardexport. Mit anderen Worten, ich denke nicht, dass es sehr sinnvoll ist, etwas, das wegfällt (Standardexport) in etwas einzuführen, das gerade hinzugefügt wird (ESM-Build).

Ich sehe import React from 'react' als neu hinzugekommenes, sondern eher als Akzeptanz der aktuellen Realität. Auch wenn es vielleicht unbeabsichtigt war und nur ein Nebeneffekt der inzwischen veralteten ESM/CJS-Interop, gibt es immer noch Tausende (Millionen?) Codezeilen, die dies tun. Die Alternative (nur benannte Exporte exportieren) sagt den Benutzern: "Wir haben einen ESM-Build in einer Nebenversion veröffentlicht, aber Sie können ihn nicht verwenden, ohne Ihren gesamten Code zu ändern", was für mich verwirrender ist, als "Standardexport entfernt zu sehen". in den Versionshinweisen einer Hauptversion.

Ich bin gespannt - wie kann ESM mit einem Standardexport abwärtskompatibel hinzugefügt werden? Dies ist schon einmal aufgetreten (zB https://github.com/facebook/react/pull/18187, das auch auf verwandte Themen verweist). Das Problem liegt bei Webpack CJS <-> ESM Interop, wo, wenn Sie CJS-Code haben, der require('react') ausführt, was Webpack in Gegenwart eines ESM react mit einem Standardexport zurückgibt, ein Objekt mit default -Eigenschaft (was bedeutet, dass sie jetzt require('react').default erfordert) unabhängig von der CJS react . Aber vielleicht ist es kein Problem, wenn Sie auch benannte exportieren? Ich denke, @TrySound ist in anderen Paketen schon einmal auf solche Probleme

Aber vielleicht ist es kein Problem, wenn Sie auch benannte exportieren?

Ja, das ist der Ansatz, an den ich denke. Siehe die letzten 40 Zeilen von https://unpkg.com/browse/@esm-bundle/react @16.13.1/esm/react.development.js - es ist eine inoffizielle Version von React, die genau dies tut und von mehreren verwendet wird Organisationen als Ersatz für das offizielle React. Keine Breaking Changes.

Aber vielleicht ist es kein Problem, wenn Sie auch benannte exportieren?

Ja, das ist der Ansatz, an den ich denke. Siehe die letzten 40 Zeilen von https://unpkg.com/browse/@esm-bundle/react @16.13.1/esm/react.development.js - es ist eine inoffizielle Version von React, die genau dies tut und von mehreren verwendet wird Organisationen als Ersatz für das offizielle React. Keine Breaking Changes.

Aber verbrauchen Sie es aus CJS-Code oder ESM? Denn es sind die CJS <-> ESM-Interop-Probleme, die sehr überraschend sein können.

@gaearon um klar zu sein; es ist sinnvoll, keinen Standardexport in dem von mir vorgeschlagenen ESM-Wrapper zu haben; jeder, der natives ESM betreibt, würde import * as React from 'react' tun, um es zu umgehen. Es ist jedoch fair, dass jeder, der dies jetzt tut, es als bahnbrechende Änderung ansehen würde, plötzlich nicht mehr über den Standardexport zu verfügen. Daher müsste es bis v17 warten, wenn Sie den Standard jetzt nicht hinzufügen möchten.

Aber verbrauchen Sie es aus CJS-Code oder ESM? Denn es sind die CJS <-> ESM-Interop-Probleme, die sehr überraschend sein können.

Ich habe es erfolgreich in Webpack aus CJS- und ESM-Dateien importiert.

jeder, der natives ESM betreibt, würde * als React von 'react' importieren, um dies zu umgehen. Es ist jedoch fair, dass jeder, der dies jetzt tut, es als bahnbrechende Änderung ansehen würde, plötzlich nicht mehr über den Standardexport zu verfügen. Daher müsste es bis v17 warten, wenn Sie den Standard jetzt nicht hinzufügen möchten.

Einverstanden. Wenn Sie von vorne beginnen, ist der Standardexport nicht erforderlich. Ohne das Hinzufügen eines Standardexports ist es jedoch nicht möglich, Phase 2 zu implementieren, ohne dass es sich um eine Breaking Change handelt. Ich persönlich wäre damit einverstanden, Phase 1 in React 16 und Phase 2-4 in React 17+ durchzuführen, obwohl ich Phase 1, 2 und vielleicht sogar 3 vorziehe (mit Hilfe des Export-Checker-Tools von

Weiter mit diesem Vorschlag . Dies scheint ein Vorschlag für einen Wechsel zu einem Dual-Paket mit vollständiger CJS- und ESM-Quelle zu sein, das die Gefahr des Dual-Pakets durch Isolieren des Zustands an anderer Stelle behandelt ( Ansatz 2 ). Im Gegensatz zur Verwendung eines ESM-Wrappers wie in meinem vorherigen RFC ( Ansatz 1 ).

Angenommen, ich habe ein paar Notizen von Dingen, die noch nicht erwähnt wurden.

  • Sie können kein Node.js-Paket haben, in dem sowohl ESM- als auch CommonJS-Dateien .js . Wenn Sie "type": "module" festlegen, müssen alle CommonJS-Dateien stattdessen die Dateierweiterung .cjs verwenden.
  • Die Gefahr des Doppelpakets von Staat und Gleichstellung ist nicht das einzige Problem, wenn sowohl CJS als auch ESM vorhanden sind. Das Laden von möglicherweise zwei Versionen desselben Pakets erhöht in diesen Fällen auch den Speicherbedarf von React, und React ist keine kleine Bibliothek. Dies ist kein Dealbreaker, aber es lohnt sich, daran zu denken.
  • Ich sehe neben dem internen Zustand von React Potenzial für eine Doppelpack-Gefahr. Wenn zum Beispiel die Implementierung der Klasse Component nicht Teil des CJS-Codes ist, in dem wir den Status teilen, sondern Teil der CJS/ESM-Bundles ist, besteht die Gefahr von instanceof Component Prüfungen in verschiedenen Bibliotheken brechen.

Sie können kein Node.js-Paket verwenden, in dem sowohl ESM- als auch CommonJS-Dateien .js verwenden. Wenn Sie "type": "module" festlegen, müssen alle CommonJS-Dateien stattdessen die Dateierweiterung .cjs verwenden.

Dies gilt für NodeJS (aber nicht für Bundler), daher müssten die Dateien im Verzeichnis cjs mit .cjs enden.

Das Laden von möglicherweise 2 Versionen desselben Pakets erhöht in diesen Fällen auch den Speicherbedarf von React

Ich sehe, wie dies die in npm veröffentlichte Tarball-Größe und damit die Gesamtgröße auf der Festplatte erhöht. Aber ich sehe nicht, wie sich das auf das Gedächtnis auswirkt. Soweit ich weiß, bringen Bundler und NodeJS keinen Code in den Speicher, der nicht über import / require() geladen wurde. Könnten Sie klarstellen, wie sich der Speicherbedarf ändern würde?

Wenn zum Beispiel die Implementierung der Component-Klasse nicht Teil des CJS-Codes ist, in dem wir den Status teilen, sondern Teil der CJS/ESM-Bundles ist, besteht die Gefahr, dass Instanzen von Komponentenprüfungen in verschiedenen Bibliotheken brechen.

Eine vorgeschlagene Lösung ( 1 , 2 ) besteht darin, dass die NodeJS esm-Implementierung einfach eine ESM-Schnittstelle ist, die den CJS-Code aufruft. Auf diese Weise gibt es nur eine Definition von Component , die jemals in Node. Phase 1 und Phase 2 würden jedoch nicht ändern, was in NodeJS läuft, daher würde dies nur für Phase 3 gelten.

Da wir (webpack) vor kurzem auch exports Feldunterstützung zu Webpack 5 hinzugefügt haben, möchte ich meine 2 Cent für dieses Thema geben:

  • Dieses Work-in-Process-Dokument enthält viele Informationen über das Feld exports bezüglich Webpack, aber auch Node.js und im Allgemeinen: https://gist.github.com/sokra/e032a0f17c1721c71cfced6f14516c62
  • Dies sind die wichtigsten Punkte im Vergleich zu Node.js:

    • webpack 5 fügt auch development und production Bedingungen hinzu, die sehr nützlich für Reagieren sind. ( process.env.NODE_ENV , obwohl es weiterhin unterstützt wird, sollte für Frontend-Code im Allgemeinen vermieden werden, es ist Node.js-spezifisch)

    • webpack (und andere Bundler) unterstützt require("esm") , was es ermöglicht, das Dual-State-Problem zu vermeiden, indem immer ESM verwendet wird (auch für require() ). webpack hat dafür eine spezielle Bedingung eingeführt: module . Für diese CommonJs- und ESM-Version muss dieselbe Schnittstelle exportiert werden. Derzeit gibt es kein anderes Ding, das das Dual-State-Problem hat, als Node.js. Ich erwarte nicht, dass wir in Zukunft etwas sehen werden, da es sich meist um ein Rückwärtskompatibilitätsproblem handelt.

      Für maximale Kompatibilität würde ich folgendes empfehlen:

Paket.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

Ermöglicht die Verwendung von .js als Erweiterung in diesen Verzeichnissen. Alternativ könnte .mjs verwendet werden, dies könnte jedoch potenzielle Nebenwirkungen haben, wenn Tools die Erweiterung überprüfen. Also .js um sicher zu gehen.

{
    "type": "module"
}

esm/wrapper.js

Dieser Wrapper wird für Node.js benötigt, um das Dual-State-Problem zu vermeiden.

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

cjs/index.js

Dies wird von Node.js verwendet, wenn die Bedingungen development und production nicht unterstützt werden.

'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 ähnlich)

Diese Wrapper werden für Node.js benötigt, um das Dual-State-Problem zu vermeiden.
Sie werden nur verwendet, wenn Node.js die Bedingungen development und production hinzufügt.

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

index.js

Für rückwärtskompatibel.

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

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

Dies wird von Tools verwendet, die das Feld exports , die Bedingung module und die Bedingungen 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 }

Ergebnisse

  • Webpack 5: ./esm/index.development.js oder ./esm/index.production.min.js
  • browserify: ./cjs/index.js
  • Webpack 4 von .mjs: ./cjs/index.js
  • andere Bündel: ./esm/wrapper.js
  • Node.js (ESM): ./cjs/index.js (erforderlich) oder ./esm/wrapper.js (Import)
  • Node.js (alt): ./cjs/index.js
  • Node.js (ESM + dev/prod): ./esm/wrapper.development.js oder ./esm/wrapper.production.min.js für den Import, ./cjs/index.development.js oder ./cjs/index.production.min.js für erforderlich

Anmerkungen

Es gibt kein esm/index.js da eine bedingte Auswahl einer Version in ESM ohne größere Kompromisse nicht möglich ist.
Tools können nur dann vollständig von Reaction ESM profitieren, wenn sie das Feld exports , die Bedingung module (wegen des Dual-State-Problems) und production / development Bedingungen (wegen des bedingten Importproblems).

Tools können teilweise von ESM-Reaktion profitieren, wenn sie das Feld module oder das Feld exports .

import { useState } from "react" oder import * as React from "react" sind technisch illegal, solange Reagieren ein CommonJs-Modul ist.
Die meisten Tools unterstützen es immer noch für Rückwärtskompatibilität, aber einige nicht, z. B. Node.js
Die einzige Möglichkeit, die überall gültige Reaktion zu verwenden, ist derzeit: import React from "react" .
Dieser Weg sollte unterstützt bleiben, da es sonst Fälle (zB Node.js 14) geben würde, in denen es keine gültige Syntax gibt, die in Reagieren jetzt und Reagieren nach ESM-Addition gültig ist.

Node.js lehnte das Hinzufügen einer Bedingung development / production für exports vorerst ab.
Das ist traurig, und ich hoffe immer noch das Beste, dass sie das irgendwann hinzufügen.
Aus diesem Grund ist die Unterstützung dafür im Feld exports oben vorbereitet.

@sokra tolle Aufschlüsselung, sehr hilfreich, danke!

Eine kleine Frage:

Node.js lehnte das Hinzufügen einer Entwicklungs-/Produktionsbedingung für den Export vorerst ab.

Soweit ich weiß, wird noch daran gearbeitet? https://github.com/nodejs/node/pull/33171 aber vielleicht missverstehe ich diesen PR

[Bearbeiten] Die obige PR, auf die ich verlinkt habe, wurde durch https://github.com/nodejs/node/pull/34637 ersetzt

[edit2] und wurde jetzt in nodejs zusammengeführt

Danke @sokra , das sind sehr hilfreiche Vorschläge.

Hier sind die Optionen, die ich sehe. Es scheint, dass alle technisch möglich sind und die Entscheidung eher eine Strategie als eine technische Umsetzung ist:

Option 1

Fügen Sie export default React zu einem React 17 ESM-Build hinzu und entfernen Sie es, sobald die CJS-Unterstützung für import React from 'react' wegfällt (vielleicht in React 18?).

Option 2

Fügen Sie nicht export default React und erstellen Sie einen React 17 ESM-Build nur mit benannten Exporten.

Option 3

Veröffentlichen Sie keinen React 17 ESM-Build. (😢) Warten Sie, bis die Unterstützung von import React from 'react'; ist, bevor Sie einen ESM-Build erstellen.

Vergleich

| | Option 1 | Option 2 | Option 3 |
| - | -------- | -------- | -------- |
| Nicht referenzierter ESM-Build | v17 | v17 | v18+ |
| package.json "module" (standardmäßig wackelt der Baum) | v17 | v18+ | v18+ |
| package.json "type" / "exports" (NodeJS verwendet ESM) | v18+ 1 | v18+ | v18+ |

  1. Es könnte möglich sein, package.json type/exports vollständig abwärtskompatibel zu implementieren. In diesem Fall könnte es Teil von React 17 sein, wenn Option 1 gewählt wird.

Ich bevorzuge Option 1, wie ich oben erläutert habe. Aber auch Option 2 finde ich ziemlich spannend. Option 3 ist natürlich weniger spannend. Nach allem, was ich in dieser Github-Ausgabe gesammelt habe, haben wir das technische Know-how, um all dies zu verwirklichen (und wahrscheinlich sogar die Arbeit!).

Die erste Reaktion auf dieses Problem war, warum ist dieses Problem auch nach 3 Jahren noch offen? Nachdem ich einen Teil davon gelesen habe, macht es Sinn, warum es so lange dauert. Die Pflege einer Bibliothek wie React ist eine riesige Aufgabe. Also

Angesichts der jüngsten Neuigkeiten zu React 17 habe ich meinen vorherigen Kommentar aktualisiert, um für zukünftige Pläne auf React 17 statt auf 16 zu verweisen.

Ich würde mich über Feedback von Leuten freuen, welche der drei oben genannten Optionen bevorzugt wird.

Ich denke, wir können das Feld exports in package.json in React 17 hinzufügen, wir könnten es wahrscheinlich auch auf frühere Versionen zurückportieren:

{
  "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"
    },
    "./": "./"
  },
}

Wir bräuchten neue esm-Bundles, obwohl dies mit Rollup nicht allzu schwierig sein sollte.

  • Die ./esm/react.development.mjs und ./esm/react.production.mjs sollten frei von process.env.NODE_ENV Schecks sein:

    • die Bedingung wird zum Import-/Bündelzeitpunkt über das Feld exports .

    • process ist eine Knoten-API, sie macht in einer Browserumgebung keinen Sinn und wird beispielsweise nicht von _default_ von Webpack 5 unterstützt.

  • Die ./esm/react.node.mjs würden die process.env.NODE_ENV Schecks behalten.
  • AFAIK unterstützt derzeit nur Webpack 5 und Node das Feld exports .

Ich denke, das hinzuzufügen ist ziemlich sicher, WDYT?

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

Das "./": "./" macht es sicher, ja, verhindert aber auch jede Kapselung, also sollten Sie es entfernen, sobald Sie ein Semver-Major haben.

FWIW babel gibt den neuen jsx-Laufzeitimport aus als

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

aber wenn Sie ein solches Modul in Node laden, beschwert es sich über die fehlende Dateierweiterung:

> 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'
}

Das Hinzufügen von exports sollte das Problem beheben 🤔

@nstepien , eine vollständige exports Karte bereitzustellen , wie Sie in Ihrem vorherigen Beitrag gezeigt haben, ist meiner Meinung nach keine Option. Was Node in Bezug auf cjs-Interop und ähnliches implementiert, passt nicht wirklich gut zum bestehenden Ökosystem. Die Gefahr von zwei Paketen ist real – insbesondere bei Paketen wie Reac, die eine einzelne Kopie davon erfordern.

Eine exports Map mit reinen Commonjs-Dateien könnte möglicherweise hinzugefügt werden, ohne etwas zu beschädigen, müsste aber auch besonders sorgfältig und mit entsprechenden e2e-Tests durchgeführt werden (angesichts der Komplexität der Dinge, um richtig zu werden).

@Andarist es funktioniert gut und ist im Fall von

Wenn alle Abhängigkeiten, transitiv oder nicht, von React unter der Kontrolle von React stehen (was in diesem Fall der Fall ist) und die ESM-Einstiegspunkte von allen nur CJS-Inhalte reexportieren, dann ja – vielleicht ist das in diesem speziellen Fall erreichbar.

Es gibt jedoch immer noch das ganze Drama, wie die tatsächliche Form des ESM-Eintritts aussehen sollte (benannt, standardmäßig, beides):

  • named-only: nicht wirklich abwärtskompatibel, da ein Großteil des Codes da draußen import React from 'react' , was auch die einzige Möglichkeit ist, React jetzt tatsächlich in Node zu importieren, wenn Sie ESM verwenden
  • nur default: nicht wirklich abwärtskompatibel, da ein Großteil des dortigen Codes import * as React from 'react' , dies wurde oft von Typprüfern und anderen Tools gefördert
  • beides: die einzige Möglichkeit, es vollständig abwärtskompatibel zu machen, sodass es mit allen aktuellen Ladestilen und beim Mischen von ESM- und CJS-Modulen über den Abhängigkeitsbaum hinweg funktionieren kann

Ich vergesse ständig die Möglichkeit von ESM-Wrappern, da sie sich wie ein Cheat anfühlen, aber auch, weil diese Technik nur funktioniert, wenn Sie alle Ihre Abhängigkeiten kontrollieren und nicht wirklich als universelle Strategie verwendet werden können, die "einfach funktionieren" würde 😢

Ich fürchte, die einzige universelle Strategie, um beides bereitzustellen, besteht in der Tat darin, Ihren gesamten eigentlichen Code in CJS zu haben und ESM-Wrapper darum zu schreiben, um benannte Exporte bereitzustellen. Code, der nicht Stateful noch stützt sich auf Identität kann nativ in beide geschrieben werden, aber das ist eine Untergruppe, eine Einschränkung.

jsx-runtime ist nicht zustandsbehaftet, oder? Sollte sicher sein, beide esm/cjs ohne Wrapper dafür zu versenden.

Sollte ich ein separates Problem für den Import von react/jsx-runtime in den esm-Knoten protokollieren?

Für Frontend-Pakete gesellt sich eine zusätzliche Herausforderung zum Dual-State-Hazard: nennen wir es den Double-Bundle-Hazard.

Wenn ESM- und CJS-Versionen exportiert werden und das Paket über require und import , werden beide Versionen gebündelt und verdoppeln die effektive Paketgröße für das Paket.

@sokra ist das in der Praxis möglich? Bei Rollup würde ich zum Beispiel annehmen, dass es, da cjs-Module in esm-Module umgewandelt werden, es dann vorziehen würde, die esm-Module zu importieren, wann immer sie verfügbar sind.

In der Praxis ist es für Bundler möglich, die der Node-Semantik folgen - wie zum Beispiel Webpack 5. Es ist wichtig, die Semantik abzugleichen, da andernfalls möglicherweise unterschiedliche Ergebnisse beim Ausführen im Node- und beim Ausführen eines Bundles-Codes auftreten würden.

Webpack 5 verarbeitet jedoch eine spezielle "module" Bedingung, die verwendet wird, um esm/cjs (die von der Exportkarte) zu deduplizieren.

Wenn wir den Vorschlag von @ljharb betrachten, ist dies jedoch nicht wirklich relevant, da er vorschlägt, dass die esm-Datei nur ein paar Zeilen Wrapper-Code sein könnte, der nur die cjs-Datei reexportieren würde.

Mit einem dünnen ESM-Wrapper gibt es keine zusätzliche Gefahr, mit oder ohne Bundler – nur die normale, die Peer-Deps über Duplikate im Diagramm vermeiden.

War diese Seite hilfreich?
0 / 5 - 0 Bewertungen