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)?
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.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älltWas 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:
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:
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,
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 vonprocess.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.
@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
undReactDOM
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 ...
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.
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.
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.
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.
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.
.js
. Wenn Sie "type": "module"
festlegen, müssen alle CommonJS-Dateien stattdessen die Dateierweiterung .cjs
verwenden.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:
exports
bezüglich Webpack, aber auch Node.js und im Allgemeinen: https://gist.github.com/sokra/e032a0f17c1721c71cfced6f14516c62development
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)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.{
"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"
}
}
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"
}
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 };
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');
}
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 };
Für rückwärtskompatibel.
module.exports = require('./cjs/index.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 }
./esm/index.development.js
oder ./esm/index.production.min.js
./cjs/index.js
./cjs/index.js
./esm/wrapper.js
./cjs/index.js
(erforderlich) oder ./esm/wrapper.js
(Import)./cjs/index.js
./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 erforderlichEs 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:
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?).
Fügen Sie nicht export default React
und erstellen Sie einen React 17 ESM-Build nur mit benannten Exporten.
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.
| | 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+ |
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.
./esm/react.development.mjs
und ./esm/react.production.mjs
sollten frei von process.env.NODE_ENV
Schecks sein:exports
.process
ist eine Knoten-API, sie macht in einer Browserumgebung keinen Sinn und wird beispielsweise nicht von _default_ von Webpack 5 unterstützt../esm/react.node.mjs
würden die process.env.NODE_ENV
Schecks behalten.--conditions
CLI-Flag anmelden: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):
import React from 'react'
, was auch die einzige Möglichkeit ist, React jetzt tatsächlich in Node zu importieren, wenn Sie ESM verwendenimport * as React from 'react'
, dies wurde oft von Typprüfern und anderen Tools gefördertIch 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.
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?