React: Formalizar las exportaciones de ES de alto nivel

Creado en 9 nov. 2017  ·  104Comentarios  ·  Fuente: facebook/react

Actualmente solo enviamos versiones CommonJS de todos los paquetes. Sin embargo, es posible que deseemos enviarlos como ESM en el futuro (https://github.com/facebook/react/issues/10021).

No podemos hacer esto fácilmente porque no hemos decidido realmente cómo se verían las exportaciones de ES de nivel superior de cada paquete. Por ejemplo, ¿ react tiene un montón de exportaciones con nombre, pero también una exportación predeterminada llamada React ? ¿Deberíamos animar a la gente a import * para sacudir mejor los árboles? ¿Qué pasa con react-test-renderer/shallow que actualmente exporta una clase (y, por lo tanto, comenzaría a fallar en el nodo si se convirtiera en una exportación predeterminada)?

Build Infrastructure React Core Team Breaking Change Discussion

Comentario más útil

Es casi 2020 ahora, me gustaría saber si hay alguna actualización del equipo oficial de FB. ¿Habría cambios relacionados con esto en React v17?

Todos 104 comentarios

Imho import * es un camino a seguir, no me opongo a tener una exportación predeterminada también, pero no debería usarse para reexportar otras cosas como en este ejemplo:

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

pero no debería usarse para reexportar otras cosas como en este ejemplo:

¿Existe una razón técnica por la cual? (Aparte de tener dos formas de hacer lo mismo).

Mi impresión es que las personas que importarían * (y no usarían el valor predeterminado) no tendrían problemas para agitar los árboles, ya que el valor predeterminado no se usaría. Pero tal vez sobreestimo Rollup, etc.

Es probable que @lukastaegert pueda responder mejor a esas preguntas. No estoy seguro si algo ha cambiado desde https://github.com/facebook/react/issues/10021#issuecomment -335128611

Además, Rollup no es el único agitador de árboles que existe, y aunque el algoritmo de agitación de árboles de webpack es peor que el del rollup, su uso es probablemente mucho mayor que el de rollup (ambas herramientas hacen un excelente trabajo, no quiero ofender a nadie , simplemente indicando hechos) y si podemos (como comunidad) ayudar a ambas herramientas a la vez, deberíamos hacerlo siempre que podamos.

¿La sacudida de árboles va a _hacer_ algo en el caso de React, dado que todo está preprocesado en un solo paquete plano? Me pregunto cuál es el estilo de importación principal para React, personalmente tiendo a tratarlo como una exportación predeterminada, por ejemplo, React.Component , React.Children pero ocasionalmente hago lo mencionado con cloneElement

Como @gaearon ya dijo en otra parte, se espera que las mejoras de tamaño en caso de reacción sean mínimas. Sin embargo, hay ventajas:

  • React.Los niños probablemente podrían ser eliminados en algunos casos (eso escuché 😉)
  • React sí mismo puede ser elevado al alcance superior por los paquetes de módulos que admiten esto. Esto podría eliminar de nuevo bastantes bytes y también podría otorgar una mejora de rendimiento tan leve. La principal mejora radicaría en el hecho de que no es necesario que haya otra variable que haga referencia a React.Component para cada módulo, sino solo una que se comparta en todas partes (así es como suele hacerlo el rollup). Además, aunque esto es solo una suposición, esto podría reducir la posibilidad de que el módulo ModuleConcatenationPlugin de webpack se salga.
  • El análisis estático para react es más fácil no solo para los paquetes de módulos, sino también para, por ejemplo, IDE y otras herramientas. Muchas de estas herramientas ya hacen un trabajo razonable en esto para los módulos CJS, pero al final, hay muchas conjeturas involucradas de su lado. Con los módulos ES6, el análisis es una obviedad.

En cuanto al tipo de exportaciones, por supuesto, solo la exportación con nombre realmente brinda el beneficio de una fácil agitación de árboles (a menos que use GCC, que podría hacer un poco más en su movimiento agresivo y tal vez el último resumen si tiene mucha suerte) . La cuestión de si también proporciona una exportación predeterminada es más difícil de decidir:

  • PRO: migración indolora para bases de código ES6 existentes (por ejemplo, lo que describe @jquense )
  • CONTRA: Dado que todo está adjunto a un objeto común, una vez que se incluye este objeto, todas sus claves se incluyen a la vez, lo que nuevamente anula cualquier intento de sacudir árboles. Incluso GCC podría tener dificultades aquí.

Como estrategia de migración de dos versiones, puede agregar una exportación predeterminada en la próxima versión con fines de compatibilidad que se declara obsoleta (incluso podría mostrar una advertencia a través de un captador, etc.) y luego eliminarla en una versión posterior.

Este también es un caso interesante: https://github.com/facebook/react/issues/11526. Si bien el parche de mono para las pruebas es un poco turbio, querremos ser conscientes de romper esto (o tener una solución alternativa).

Vine aquí a través de esta conversación de Twitter . Para mí, hay una respuesta clara y correcta a esta pregunta: React y ReactDOM solo deberían exportar exportaciones con nombre. No son objetos que contengan estado, o que otras bibliotecas puedan mutar o adjuntar propiedades (no obstante el # 11526); la única razón por la que existen es como un lugar para 'poner' Component , createElement y así sucesivamente. En otras palabras, espacios de nombres, que deben importarse como tales.

(También les facilita la vida a los agrupadores, pero eso no es ni aquí ni allá).

Por supuesto, eso presenta un cambio importante para las personas que actualmente usan una importación y transpilación predeterminada. @lukastaegert probablemente tenga la idea correcta aquí, usando

Sin embargo, no tengo una sugerencia preparada para el # 11526. Quizás el envío de ESM tendría que esperar a la v17 por ese motivo de todos modos, en cuyo caso no habría necesidad de preocuparse por las advertencias de obsolescencia.

A la gente realmente le ha gustado

import React, { Component } from 'react'

por lo que convencerlos de que lo abandonen podría resultar difícil.

Supongo que esto no es tan malo, aunque sea un poco extraño:

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

Para aclarar, necesitamos que React esté dentro del alcance (en este caso, como un espacio de nombres) porque JSX se transpila a React.createElement() . Podríamos romper JSX y decir que depende de la función global jsx() lugar. Entonces las importaciones se verían así:

import {jsx, Component} from 'react';

lo cual quizás esté bien, pero es un gran cambio. Esto también significaría que las compilaciones de React UMD ahora deben configurar window.jsx también.

¿Por qué sugiero jsx lugar de createElement ? Bueno, createElement ya está sobrecargado ( document.createElement ) y aunque está bien con el calificador React. , sin reclamarlo en el global es demasiado. Tbh no estoy muy emocionado con ninguna de estas opciones, y creo que probablemente este sería el mejor término medio:

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

y mantener JSX transpilando a React.createElement por defecto.

Confesión: Siempre me pareció un poco extraño que tengas que importar explícitamente React para usar JSX, aunque no estés usando ese identificador en ninguna parte. Quizás en el futuro, los transpilers podrían insertar import * as React from 'react' (configurable por el bien de Preact, etc.) al encontrar JSX, si aún no existe. De esa manera solo necesitarías hacer esto ...

import { Component } from 'react';

... y la importación del espacio de nombres se haría automáticamente.

Quizás en un futuro lejano. Por ahora, necesitamos asegurarnos de que los transpilers funcionen con otros sistemas de módulos (CommonJS o globales). Hacer esto configurable también es un obstáculo y divide aún más a la comunidad.

Lo que sugirió @ Rich-Harris (insertar una importación específica cuando se usa jsx) lo hace fácilmente el complemento transpilers. La comunidad tendría que actualizar su babel-plugin-transform-react-jsx y eso es todo. Y, por supuesto, incluso las configuraciones existentes seguirían funcionando si solo una agrega import * as React from 'react'; al archivo.

Por supuesto, debemos considerar otros sistemas de módulos, pero no parece un problema difícil de resolver. ¿Hay alguna trampa específica en mente?

Por supuesto, debemos considerar otros sistemas de módulos, pero no parece un problema difícil de resolver. ¿Hay alguna trampa específica en mente?

No sé, ¿cuál es su sugerencia específica sobre cómo manejarlo? ¿Cuál sería el valor predeterminado para el complemento Babel JSX?

A la gente realmente le ha gustado

import React, { Component } from 'react'

¿Que gente? Ven para que pueda burlarme de ti.

Hice eso mucho 🙂 Estoy bastante seguro de que también he visto esto en otros lugares.

Por el momento, el React.createElement predeterminado es

Creo que como los módulos es son básicamente la forma estándar (aunque aún no todos los han adoptado) de hacer módulos, es razonable suponer que la mayoría lo usa (o debería). La gran mayoría ya usa varias herramientas de pasos de compilación para crear sus paquetes, lo cual es aún más cierto en esta discusión porque estamos hablando de transpilar la sintaxis jsx . Cambiar el comportamiento predeterminado del complemento jsx a la inserción automática de React.createElement en el alcance es, en mi humilde opinión, algo razonable. Estamos en el momento perfecto para este cambio con babel @ 7 próximamente (-ish). Con la reciente adición de babel-helper-module-imports , también es más fácil que nunca insertar el tipo correcto de importación (es / cjs) en el archivo.

Tener esto configurable para rescatar al comportamiento actual (asumiendo que está presente en el alcance) parece realmente un cambio menor en la configuración necesario para una minoría de usuarios y una mejora (seguro, no una gran, pero aún así) para la mayoría.

¿Deberíamos alentar a la gente a importar * para agitar mejor los árboles?

Gracias a @alexlamsl uglify-es ha eliminado la penalización de export default en escenarios comunes:

$ 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, eso es genial nuevo 👏 ¿ uglify-es considera estable ahora? Recuerdo que mencionaste hace unos meses que aún no está allí, pero puedo recordar eso incorrectamente, así que no estoy seguro.

De todos modos, eso es todo y bueno en un mundo de acumulación, pero teniendo en cuenta que React se incluye principalmente en aplicaciones y esas usan principalmente webpack que no realiza elevación de alcance de forma predeterminada, todavía diría que Se debe evitar exportar un objeto por defecto para ayudar a otras herramientas además de uglisy-es + rollup en sus esfuerzos por producir paquetes de tamaños más pequeños. También para mí es semánticamente mejor evitar esto: lo que las bibliotecas realmente hacen en tales casos es proporcionar un espacio de nombres y está mejor representado cuando se usa import * as Namespace from 'namespace'

¿Se considera que uglify-es es estable ahora?

Tan estable como cualquier otra cosa en el ecosistema JS. Más de 500K descargas por semana.

eso es todo y bueno en un mundo acumulativo, pero teniendo en cuenta que React se incluye principalmente en aplicaciones y esas usan principalmente un paquete web que no eleva el alcance de forma predeterminada

De todos modos, es una opción. De todos modos, los valores predeterminados de Webpack no son ideales; tienes que usar ModuleConcatenationPlugin como sabes.

Añadiendo unos centavos aquí:

  • Estoy totalmente de acuerdo con @ Rich-Harris en que semánticamente, las exportaciones con nombre son la elección correcta
  • Realmente no me gusta import React from 'react' o import * as React from 'react' solo para poder usar la sintaxis JSX. En mi opinión, este diseño viola claramente el principio de segregación de interfaz, ya que obliga a los usuarios a importar todo React solo para poder usar la parte createElement (aunque es cierto que con una exportación de espacio de nombres, un paquete como Rollup lo hará). eliminar las exportaciones innecesarias de nuevo)

Entonces, si estamos en un punto en el que podríamos tomar decisiones de cambio radical, recomendaría cambiar esto para que JSX dependa de una función única (global o importada). Lo habría llamado createJSXElement() , que en mi opinión lo describe incluso mejor que createElement() y ya no necesita el contexto React para tener sentido. Pero en un mundo donde cada byte cuenta, jsx() probablemente también esté bien.

Esto también desacoplaría finalmente JSX de React de tal manera que otras bibliotecas pueden optar por admitir JSX utilizando la misma transformación y proporcionando una función jsx . Por supuesto, aquí tiene mucha responsabilidad guiar a innumerables aplicaciones establecidas a través de tal transformación, pero desde un punto de vista arquitectónico, aquí es donde creo que React y JSX deberían dirigirse. ¡Usar Babel para hacer el trabajo pesado de tal transformación me parece una gran idea!

Personalmente, no veo mucha ganancia en migrar a jsx helper, ya que en mi humilde opinión el complemento babel debería importarlo desde el paquete react , por lo que el nombre del ayudante real no importa: el resto es solo cuestión de tenerlo configurable.

Esto probablemente sea un poco tangencial a la discusión principal, pero tengo curiosidad por saber qué tan bien funcionan los módulos ES con la verificación de process.env.NODE_ENV para exportar condicionalmente paquetes dev / prod. Por ejemplo,

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

Puede que me esté perdiendo algo obvio aquí, pero estoy luchando por ver cómo traducir este patrón en módulos ES.

@NMinhNguyen Las exportaciones condicionales no son posibles con los módulos ES.

process.env.NODE_ENV embargo, los cheques

@Andarist @milesj Gracias por confirmar mi sospecha :)

process.env.NODE_ENV embargo, los cheques

De la publicación del blog React 16, pensé que los cheques process.env.NODE_ENV se sacaron a la parte superior a propósito (en lugar de que sean más granulares, que es lo que están en la fuente, si no me equivoco ), para ayudar al rendimiento en Node.js?

Mejor renderizado del lado del servidor

React 16 incluye un renderizador de servidor completamente reescrito. Es muy rapido. Es compatible con la transmisión , por lo que puede comenzar a enviar bytes al cliente más rápido. Y gracias a una nueva estrategia de empaquetado que compila process.env cheques (lo crea o no, leer process.env en Node es muy lento), ya no necesita agrupar React para obtener un buen servidor- rendimiento de renderizado.

Por ejemplo, no estoy seguro de cómo se podría usar el campo module en package.json y diferenciar entre dev / prod para ESM mientras se mantienen los paquetes de ES planos y sin afectar el rendimiento de Node.js

Por ejemplo, no estoy seguro de cómo se podría usar el campo del módulo en package.json y diferenciar entre dev / prod para ESM mientras se mantienen los paquetes ES planos y no se afectan el rendimiento de Node.js

Sin duda, esto es un inconveniente, porque no existe una forma estándar en este momento para hacer esto. OTOH es solo una cuestión de herramientas, es posible (y es bastante fácil) compilar esto en los pasos de compilación de su aplicación incluso hoy. Ofc, sería más fácil si el paquete pudiera exponer las compilaciones dev / prod y el solucionador simplemente supiera cuál elegir, pero tal vez sea solo una cuestión de llevar esta idea a los autores de herramientas.

Para clase:

import Component from 'react/Component'

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

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

Donde transform usará super.createElement () para transformar a jsx o usar estático Component.createElement ().

Para componentes sin estado:

import jsx from 'react/jsx'

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

¿Es posible usar literal de plantilla etiquetada?

Es de esperar que el nodo acepte este PR https://github.com/nodejs/node/pull/18392

Estamos de acuerdo aquí con @ Rich-Harris.

Solo dejo un comentario en este hilo que realmente no se ha mencionado específicamente.

Estoy en una situación en la que no estoy usando ningún paquete y solo quiero importar react y varios componentes para usarlos de forma nativa a través del navegador ( <script type="module" src="..."> ), es decir

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

Por lo que puedo decir, esto no es posible hoy. En su lugar, tengo que incluir la versión UMD de react a través de una etiqueta <script> del CDN y luego asumir su presencia en la ventana en cualquier módulo <script type="module"> que escriba:

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

Tener una importación de reacción desde un CDN sería fantástico. Haría que la creación de prototipos en el navegador fuera muy rápida y sencilla, sin dejar de poder mantener la separación de archivos. Una cosa que siempre sentí que estaba sacrificando al usar React sin un paquete era la capacidad de separar componentes (y otras funciones de utilidad, etc.) por archivo. Pero ahora, con la compatibilidad del navegador con los módulos nativos de ES, puedo escribir mis componentes React en archivos separados y hacer que el navegador los consuma tal como están escritos. De acuerdo, eso es si no estoy usando JSX, pero incluso si estuviera usando JSX, podría transpirar todos los archivos en su lugar a través de un paso de compilación y todas mis importaciones seguirían funcionando en el navegador.

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

Estoy seguro de que algunas personas argumentarían que escribir esa URL de CDN todo el tiempo es un problema (un problema que algunas personas están tratando de solucionar) pero las compensaciones merecen la pena. Cambiar / actualizar esa URL es una simple búsqueda / reemplazo. Para mi caso de uso, esto compensa el problema de configurar un paquete.

Si React tuviera soporte para algo como esto, no habría necesidad de herramientas. Solo estoy usando el navegador. Podría enviar un código como este en algunos proyectos personales que asumen que los navegadores modernos y el uso reaccionan como una mejora progresiva en la página. Lo que hace que esto sea fantástico es que cuando vuelvo a la base de código en 12 meses, no tengo que cambiar un montón de API de herramientas o incluso tener NPM como administrador de paquetes. Solo estoy usando API del navegador, nada más.

FWIW: si / cuando React se envía con soporte como este, creo que podría ser muy valioso mostrar cómo podría usar React de esta manera en los documentos, enseñando que puede aprovechar React y su modelo de componentes separando la lógica de cada componente a través de su propio archivo y no necesita un paquete para hacerlo, solo use <script type="module"> nativo, importe React desde un CDN (o su propia copia local), ¡y listo! "

Ahora, todos los navegadores modernos, incluidas las versiones móviles, son compatibles con ESM. ESM ya no es un sistema de módulos del futuro, sino un estándar de facto actual.

Tenga en cuenta que no proporcionar el módulo estandarizado es un problema crítico, especialmente para una biblioteca web estándar de facto.

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

Este es el código típico para aplicar bibliotecas de React, y el hecho es que en realidad no hay bibliotecas que se puedan importar, sino que los transpilers y empaquetadores de terceros emulan el proceso de importación.

Se ha justificado un poco no proporcionar el ESM real ya que los navegadores no habían admitido el ESM nativo de todos modos, pero obviamente, el tiempo se acabó y ahora es el momento de proporcionar ESM como se especifica, el código de muestra típico a import .

Empecé a trabajar en esto aquí y aquí.

@ TrySound Gracias por tu contribución.
¿Hay algún lugar para tomar y probar la compilación de ESM?

Está listo solo para el paquete react-is.

@PruebaSonido
Ok, encontré tu rama https://github.com/TrySound/react/tree/react-is-esm , y la he construido, y ahora sé lo que querías decir. Esperamos también react-dom .

Creo que la comunidad de React discutió este tema lo suficiente por un tiempo.
https://discuss.reactjs.org/t/es6-import-as-react-vs-import-react/360/

Por favor, decida la especificación oficial del módulo ES6 y publíquela pronto.

@kenokabe Estamos en camino. No nos fuerces por favor. No es así de fácil.

El plan actual es migrar todos los paquetes con solo exportaciones con nombre. Este cambio no afectará el código de las bibliotecas y no debería introducir cambios importantes, ya que los documentos también utilizan exportaciones con nombre.

Para otros paquetes, necesitamos manejar exportaciones predeterminadas y con nombre que funcionan de manera diferente con varias herramientas.

@ TrySound Mis disculpas.
No quise decir contigo, ya que la mención principal de este tema es

No podemos hacer esto fácilmente porque no hemos decidido realmente cómo se verían las exportaciones de ES de nivel superior de cada paquete. Por ejemplo, ¿react tiene un montón de exportaciones con nombre, pero también una exportación predeterminada llamada React? ¿Deberíamos alentar a la gente a importar * para agitar mejor los árboles?

y, el día mencionado fue hace un tiempo, y pensé que se había discutido en la comunidad React, por lo que quería sugerir que la decisión sería clara. ¡Gracias!

Quiero recibir alguna actualización sobre esto ...

Estoy usando webpack v4 para empaquetar nuestra aplicación, mientras que mi IDE intellisense (WebStorm) me sugiere usar import * as React from 'react'; mientras mi compañero de trabajo me pide que cambie import React from 'react'; en una revisión de código. Ambos funcionan bien, así que pensé que estaba diciendo tonterías, pero para hacerlo feliz, lo cambiaré de todos modos. Así también encuentro este hilo.

Aunque por curiosidad, comparo las diferencias en el tamaño de compilación final entre ellos (con React 16.8.1):

En import * as React from 'react'; : 6,618,723 bytes
En import React from 'react'; : 6,619,077 bytes

Entonces, obviamente, tuvo algunas diferencias, aunque marginales. (nota. Yo hice lo mismo con propTypes )

Si mi comprensión en este hilo es correcta, estaría a favor de tener import * as React from 'react'; , ¿verdad? Porque (1) sí, ahorró algo de tamaño; (2) ESM es una forma estandarizada, por lo que no más sobras de CJS. Si ese es el caso, me gustaría cambiar esto hoy y alinearme con mi IDE.

@leoyli A largo plazo, sí. Pero primero habrá exportaciones con nombre y predeterminadas para no romper el código existente.

Tomé el asunto en mis propias manos aquí, algo como un experimento, ya que ya no estoy usando un paquete en mis proyectos y quería usar react (directamente desde unpkg.com como puede hacerlo con otras bibliotecas como Vue, Hyperapp, etc.) . Esto es lo que se me ocurrió, nada lujoso, solo un umd editado a mano:

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

Un módulo ES6 que expone la última versión de React y ReactDOM

Como se describe en el README, esto es principalmente un POC, pero para las personas que no pueden esperar a que aterrice esta construcción, entonces es una construcción 16.8.3 que incluye ganchos, suspenso, perezoso, etc. y funciona como se espera al hacer:

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

Tal vez algunos de ustedes en este hilo lo encuentren útil ... personalmente, he estado usando este enfoque para crear un proyecto de inicio de reacción sin pasos de compilación . También es todavía un trabajo en progreso.

@lukejacksonn También hemos estado usando una solución de este tipo en producción, mientras que nuestro enfoque fue diferente en el sentido de que es más un script transformador para la versión UMD de React y ReactDOM en su proyecto actual. Y que genera estos archivos por separado, por lo que para la mayor parte del código debería ser un reemplazo directo. Si está interesado https://github.com/wearespindle/react-ecmascript y también puede cargarlo desde unpkg https://unpkg.com/react-ecmascript/

@ PM5544 ¡Oh, vaya ... esta es una solución mucho más completa que la mía! Buen trabajo 💯

Cosas increíbles @ PM5544. Me encantaría saber más sobre esto algún día. ¿Quizás una aparición especial en Xebia?
Recientemente adopté el paquete para empaquetar mis paquetes de código abierto, que es compatible con UNPKG.
¿Alguien conoce un buen artículo sobre la carga de dependencias desde UNPKG directamente en lugar de usar un paquete?

¡Actualmente estoy escribiendo uno y lo daré como una charla en React Norway en junio también!

@ TrySound ¿Ha habido alguna actualización sobre esto desde febrero? ¿Qué queda para que este problema se mueva? ¿Puedo participar para que este problema se cierre con algún trabajo de codificación? Ya firmé la CA y hoy tengo tiempo disponible para trabajar en ella.

Esto debe fusionarse primero https://github.com/facebook/react/pull/15037

@ TrySound Está bien, gracias, he enviado mi oferta de ayuda para ese hilo.

Cuando opta por una exportación de React predeterminada, puede optar por este enfoque:

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

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

Esto hace que sea posible analizar estáticamente que la exportación predeterminada es un objeto de espacio de nombres que permite la agitación de árboles para estas construcciones en el paquete web 5 y el resumen:

import React from "react";

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

Estoy en el chat de rollup gitter durante 1,5 años y este tipo de problemas surgen cada 2 semanas más o menos ...

Por cierto, @lukejacksonn hizo un gran trabajo en la oficial de es-react , lo recomiendo encarecidamente.

Por cierto, @lukejacksonn hizo un gran trabajo en la oficial de es-react , lo recomiendo encarecidamente.

Me pregunto por qué el equipo oficial de FB no hace nada al respecto.

También esperaba que esto generara algo de presión para hacer avanzar las cosas, y supongo que también lo fue el propio @lukejacksonn . Sin embargo, según tengo entendido, en realidad recibió algo de apoyo del equipo de React para construir su bifurcación a partir de las fuentes originales, por lo que parece haber al menos cierto interés en esto.

Yo también esperaba esto. En realidad, no recibí casi ningún apoyo del equipo de reacción en la creación del paquete (además de algunas amables palabras de aliento de @threepointone). Recientemente, un colega aquí en Formidable me ayudó a construir el paquete programáticamente, lo cual es una mejora con respecto a hacerlo a mano cada vez que se lanza una nueva versión de react, pero da como resultado una salida mucho menos limpia en la pestaña de red, por lo que no estoy seguro de si lo hará. Quédate así todavía. ¡Veremos!

Es casi 2020 ahora, me gustaría saber si hay alguna actualización del equipo oficial de FB. ¿Habría cambios relacionados con esto en React v17?

Para aquellos que necesitan un módulo ES actualizado React NOW, pruebe @pica/react , que ya está en v16.13.x
https://www.npmjs.com/package/@pika/react
https://www.npmjs.com/package/@pika/react -dom
https://github.com/pikapkg/react

Quizás en un futuro lejano.

Ok, ya en el futuro. ¿Es ahora en un futuro próximo?

@gaearon ¿cuál es el bloqueador para decidir cómo estructurar las exportaciones (exportación predeterminada vs exportaciones con nombre)? ¿Hay algo que la comunidad pueda ayudarlo a tomar esta decisión?

Al parecer esa decisión ya se tomó hace un tiempo: # 18102. Este problema se puede cerrar ahora.

Daré una pequeña actualización sobre esto.

Sin exportaciones predeterminadas

Nuestro plan final es alejarnos por completo de las exportaciones predeterminadas:

import { useState } from 'react';

En ese mundo, esto no funcionaría:

import React from 'react'; // no

Esto funcionaría aunque es un poco ruidoso:

import * as React from 'react';

Pero aquí está el truco. En realidad, no habría muchas razones para importar React .

Importación automática de JSX

La razón por la que la gente importa React hoy se debe principalmente a JSX. Pero @lunaruan está terminando de trabajar en la nueva transformación JSX y codificaciones relacionadas, lo que elimina la necesidad de hacerlo.

Entonces irías de esto:

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

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

a esto:

import { useState } from 'react';

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

JSX inserta la importación correcta automáticamente bajo el capó, por lo que no es necesario React en el alcance.
Esto es lo que hace tolerable el movimiento para eliminar las exportaciones predeterminadas. Simplemente no los necesita tanto.

Módulos ES

Implementar ESM más allá de una pequeña porción de entusiastas es un desafío. El amplio ecosistema no está realmente listo y hay muchas formas en las que las cosas salen mal con diferentes combinaciones de herramientas. La forma en que interactúan CJS y ESM es muy compleja, y esa interoperabilidad (y cómo falla) es la fuente de la mayoría de estos problemas.

Entonces, nuestro pensamiento actual es que cuando vayamos a ESM, es posible que deseemos intentarlo hasta el final. Sin CJS en absoluto, o separados en un paquete heredado compatible. Esto no sucederá en React 17 y es poco probable en 18, pero es plausible intentarlo en React 19.

Para cualquiera que busque una alternativa a la compilación de ESM de @pika/react , consulte https://github.com/esm-bundle/react y https://github.com/esm-bundle/react-dom. La diferencia es que se pueden usar en navegadores sin un polyfill de mapa de importación: el import React from 'react'; dentro del código fuente de react-dom se modifica para importar React desde una URL de CDN completa. Otra diferencia es que las nuevas versiones se publican automáticamente cada vez que se publica una nueva versión de reacción, sin ningún paso manual.

Demostración de Code Sandbox: https://codesandbox.io/s/gifted-roentgen-qcqoj?file=/index.html

Algunas personas han desafiado la noción de que el ecosistema no está listo. No tengo un contexto completo sobre esto, pero si cree que este es un buen momento para comenzar a hacer cambios, le agradecería que echara un vistazo a https://github.com/reactjs/rfcs/pull/38 y expresara cualquier preocupaciones al respecto. ¿Es eso más o menos lo que tenía en mente o preferiría un enfoque diferente?

Pero aquí está el truco. En realidad, no habría muchas razones para importar React.

La hay si está utilizando TypeScript, y seguirá siéndolo en el futuro previsible. Hasta que TS conozca este nuevo comportamiento mágico de babel, los desarrolladores tendrán que continuar importando React explícitamente, y necesitan saber cuál es la declaración de importación correcta.

JSX inserta la importación correcta automáticamente bajo el capó, por lo que no es necesario reaccionar en el alcance.

s / JSX / El nuevo complemento babel react jsx transform /. JSX es una extensión de sintaxis de JavaScript, por sí solo no hace nada.

La hay si está utilizando TypeScript, y seguirá siéndolo en el futuro previsible. Hasta que TS conozca este nuevo comportamiento mágico de babel, los desarrolladores tendrán que continuar importando React explícitamente, y necesitan saber cuál es la declaración de importación correcta.

El equipo de TypeScript está al tanto de este cambio y se está rastreando en https://github.com/microsoft/TypeScript/issues/34547. Estamos en estrecho contacto con ellos, así que tenga la seguridad de que no es un ciudadano de segunda clase para nosotros.

s / JSX / El nuevo complemento de transformación babel react jsx /

¡Sí, esto es lo que quise decir!

Le agradecería que revisara reactjs / rfcs # 38 y expresara cualquier inquietud al respecto. ¿Es eso más o menos lo que tenía en mente o preferiría un enfoque diferente?

Parte del texto original de la RFC está desactualizado. NodeJS permite que ESM se ejecute en archivos .js lugar de .mjs ahora, cuando especifica un "type" en su package.json. ( documentos ).

Específicamente, el texto original de RFC dice lo siguiente que no es cierto:

El código ESM debe estar dentro de un archivo .mjs

Después de hablar con @frehner , aquí está nuestra propuesta sobre cómo React podría convertirse gradualmente a ESM. Tenga en cuenta que no combinamos el problema de las exportaciones con nombre / predeterminado con la publicación de una versión ESM de React. @gaearon ha aclarado que la exportación predeterminada eventualmente desaparecerá, pero eso no se incluye en nuestra propuesta hasta la Fase 4.

| | Fase 1 | Fase 2 | Fase 3 | Fase 4 |
| - | -------- | -------- | -------- | ------- |
| ¿ESM publicado? | ✔️ | ✔️ | ✔️ | ✔️ |
| package.json "module" | ❌ | ✔️ | ✔️ | ✔️ |
| webpack / rollup use esm 1 , 2 | ❌ | ✔️ | ✔️ | ✔️ |
| package.json "exports" | ❌ | ❌ | ✔️ | ✔️ |
| package.json "type" | ❌ | ❌ | ✔️ | ✔️ |
| NodeJS usa esm | ❌ | ❌ | ✔️ | ✔️ |
| ¿Rompiendo el cambio? | ❌ | ❌ | ❓ | ✔️ |
| ¿Se ha ido la exportación predeterminada? | ❌ | ❌ | ❌ | ✔️ |
| Extensiones de archivo necesarias en las importaciones | ❌ | ❌ | ❌ | ❌ |
| extensiones de archivo mjs | ❌ | ❌ | ❌ | ❌ |

Creo que hay un argumento válido para combinar la Fase 1 y la Fase 2 juntas, ya que la Fase 1 realmente solo se dirige a los entusiastas. Sin embargo, los dividí porque creo que separar las fases da la oportunidad de implementar ESM muy lentamente de una manera que no rompa inmediatamente la CRA y todo el ecosistema sin primero darles a los primeros usuarios la oportunidad de informar problemas y encontrar soluciones. .

@joeldenning ¿cuál sería el momento aproximado de las fases? ¿Se basa solo en el tiempo o están relacionados con algunos puntos de control del tiempo en el ecosistema? ¿ require('react') todavía funcionaría en la Fase 3?

¿Cuál sería el calendario aproximado de las fases? ¿Se basa solo en el tiempo o están relacionados con algunos puntos de control del tiempo en el ecosistema?

El tiempo para todas las fases solo está limitado por el trabajo a realizar y el calendario de lanzamiento de React. Tengo entendido que todas las cosas propuestas son compatibles con bundlers y nodejs, con las alternativas adecuadas cuando se utilizan versiones anteriores que no las admiten.

¿Requeriría ('reaccionar') seguir funcionando en la Fase 3?

Pienso que si. Consulte https://nodejs.org/api/esm.html#esm_dual_commonjs_es_module_packages y https://nodejs.org/api/esm.html#esm_package_entry_points. Por supuesto, es muy posible que no conozca todos los casos de esquina para todo, pero tengo entendido que el camino de transición que he propuesto "funcionará en todas partes". Quizás esas son las últimas palabras famosas 😄

Una aclaración adicional, aquí está lo que proponemos que se vería el tarball publicado para React:

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

Y aquí hay una aproximación de lo que sería el package.json al final:

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

^ Esto no está completamente pensado y perfeccionado, pero lo estoy compartiendo para darle un contexto concreto a la propuesta.

Entonces, React sufre el peligro de paquete dual porque tiene estado (ganchos), por lo que este estado debería aislarse cuidadosamente. O debería residir en un pequeño archivo CJS que podría ser importado por las entradas de CJS y ESM o tal vez haya una manera de cargar .json y mutarlo con ese estado de ambas entradas (no estoy 100% seguro acerca de el segundo enfoque).

También creo que es importante enumerar en su tabla la adición de exportaciones con nombre, lo que ya sucedería en la Fase 1.

Todavía no estoy 100% seguro si no me he perdido algunos casos de esquina, este tema es muy complejo. Al mismo tiempo, el cronograma debe estar vinculado a los puntos de control del ecosistema: la Fase 3 solo se puede enviar una vez que exports tenga suficiente soporte en los paquetes; de lo contrario, creo que esto podría generar problemas potenciales.

React sufre el peligro de paquete dual porque tiene estado (ganchos), por lo que este estado debería aislarse cuidadosamente.

Buen punto, no había considerado esto ( más información ). En ese caso, quizás sea útil su sugerencia de un archivo compartido entre las compilaciones de CJS / ESM. O quizás la versión de ESM no es una copia completa de react, sino solo la interfaz pública de ESM que llama a la compilación de CJS.

También creo que es importante enumerar en su tabla la adición de exportaciones con nombre, lo que ya sucedería en la Fase 1.

Sin embargo, parece que este ya es el caso en el código fuente. Por lo que puedo decir, el código fuente ya exporta cosas como exportaciones con nombre.

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

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

Todavía no estoy 100% seguro si no me he perdido algunos casos de esquina, este tema es muy complejo. Al mismo tiempo, el cronograma debe estar vinculado a los puntos de control del ecosistema: la Fase 3 solo se puede enviar una vez que las exportaciones tengan suficiente soporte en los paquetes; de lo contrario, creo que esto podría generar problemas potenciales.

Estoy de acuerdo sobre la Fase 3: es por eso que puse un signo de interrogación sobre si fue un cambio importante. Sé que agregar exportaciones de package.json a menudo ha sido un cambio radical para otros paquetes en el ecosistema. Y el tema es definitivamente complejo. Una cosa a tener en cuenta es que el orden de la Fase 3 y 4 podría intercambiarse, si se desea. Creo que quienes implementan cada fase tendrían que hacer pruebas muy, muy exhaustivas de muchas versiones de webpack, rollup, nodejs, etc. No estoy diciendo que el trabajo por hacer sea trivial, solo digo que sí creo que hay es probable que haya una vía de transición aquí :)

Sin embargo, parece que este ya es el caso en el código fuente. Por lo que puedo decir, el código fuente ya exporta cosas como exportaciones con nombre.

Ah, correcto, en este caso debería haber una fila de tabla para agregar export default 😂, ya que actualmente funciona en la mayoría de los paquetes y es popular en la naturaleza, pero agregar una verdadera entrada de ESM sin proporcionar default sería romper esos usos.

en este caso, debería haber una fila de la tabla para agregar la exportación predeterminada

Sí, buen punto. Agregaré eso. Creo que un PR haciendo eso "parecería malo", pero lo veo como simplemente aceptar la interfaz pública actual por lo que es, con planes para mejorar en el futuro.

Debemos tener en cuenta que tan pronto como se publique alguna versión, no hay vuelta atrás y cualquier cambio semántico se rompería. Dado que no lanzamos mayores con mucha frecuencia, deberíamos considerar cómo agrupar los cambios para que haya menos rotación.

También hay una cuestión de cómo se manejaría la división de construcción de desarrollo / producción. Y, de hecho, es muy importante preservar la naturaleza con estado de la compilación de React.

Debemos tener en cuenta que tan pronto como se publique alguna versión, no hay vuelta atrás y cualquier cambio semántico se rompería. Dado que no lanzamos mayores con mucha frecuencia, deberíamos considerar cómo agrupar los cambios para que haya menos rotación.

Buenos puntos. Mi idea es agregar un export default React explícito a una compilación de ESM que se publica en React 16. Creo que el PR podría levantar algunas cejas y podría ser controvertido, ya que ese no es el destino que se ha decidido. Sin embargo, mi opinión es que podemos tener una compilación de ESM en React 16 y luego eliminar el export default en una futura versión principal. Para mí, usar react a través de import React from 'react'; es tan abrumadoramente común que exportar el valor predeterminado en la compilación de ESM es simplemente "aceptar dónde estamos". Una versión principal futura lo eliminaría.

También relacionado con la minimización de cambios importantes: las fases 3 y 4 podrían ser parte de la misma versión principal. Es posible que la fase 3 se pueda hacer de una manera totalmente compatible con versiones anteriores, en cuyo caso también podría colarse en una versión 16. Pero ese es más complicado y no sé lo suficiente para tener confianza en eso.

También hay una cuestión de cómo se manejaría la división de construcción de desarrollo / producción.

Esto es algo que pasé por alto. No sé cómo hacer esto con ESM en ambos paquetes y en NodeJS, pero investigaré un poco para ver qué es posible. Encontré esta propuesta muerta , pero buscaré las vivas :)

Y, de hecho, es muy importante preservar la naturaleza con estado de la compilación de React.

Acordado. Una cosa a tener en cuenta es que la naturaleza con estado de la compilación de React solo necesita resolverse en la Fase 3, no en las Fases 1 y 2. Las opciones que @Andarist y yo sugerimos funcionarían para resolverlo.

El enfoque más fácil y compatible con versiones anteriores por ahora es agregar un contenedor ESM simple para permitir importaciones con nombre.

Estaría feliz de hacer un PR para agregar esto a React, asegurándome de que la adición del campo "exportaciones" no sea un cambio rotundo (escribí una herramienta, npx ls-exports , lo que facilita la determinación de esto). No ayudará a las personas que intentan usar ESM sin un proceso de compilación, pero ese es un problema que los paquetes individuales no son capaces de resolver de todos modos.

@gaearon, ¿eso sería útil? Podría aterrizar en React 16 como semver-minor.

La compilación de CJS continuaría usando la detección de entorno, como lo hace ahora, de modo que el problema (difícil, sin resolver) en ESM no tiene que resolverse todavía.

Sin embargo, mi opinión es que podemos tener una compilación de ESM en React 16 y luego eliminar el valor predeterminado de exportación en una versión principal futura.

Otra preocupación es que el aumento del número de configuraciones ejerce presión sobre el ecosistema. Por ejemplo, creo que si lanzamos una compilación de ESM, no debería incluir algo que definitivamente eliminaremos en la próxima versión, como una exportación predeterminada. En otras palabras, no creo que tenga mucho sentido introducir algo que va a desaparecer (exportaciones predeterminadas) en algo que se acaba de agregar (compilación de ESM).

Sin embargo, mi opinión es que podemos tener una compilación de ESM en React 16 y luego eliminar el valor predeterminado de exportación en una versión principal futura.

Otra preocupación es que el aumento del número de configuraciones ejerce presión sobre el ecosistema. Por ejemplo, creo que si lanzamos una compilación de ESM, no debería incluir algo que definitivamente eliminaremos en la próxima versión, como una exportación predeterminada. En otras palabras, no creo que tenga mucho sentido introducir algo que va a desaparecer (exportaciones predeterminadas) en algo que se acaba de agregar (compilación de ESM).

Creo que depende de cuánto tiempo dure el próximo lanzamiento. Si hablamos de una semana de diferencia, parece razonable. Sin embargo, si hablamos de meses / años, entonces no veo por qué sería malo sacarlo durante tanto tiempo.

La preocupación en este caso es que cuanto más tiempo esté disponible, más personas dependerán de él en la forma actual. Y luego, introducir un cambio importante allí les haría más difícil actualizar React. Ahora no hay solo una migración de ESM, sino varias, y algunas personas se quedarán atrás porque saltaron demasiado pronto y luego no tienen los recursos para otra migración. Lo cual, en el caso de ESM, se extenderá por todo el ecosistema.

Creo que si lanzamos una compilación de ESM, no debería incluir algo que definitivamente eliminaremos en la próxima versión, como una exportación predeterminada. En otras palabras, no creo que tenga mucho sentido introducir algo que va a desaparecer (exportaciones predeterminadas) en algo que se acaba de agregar (compilación de ESM).

No veo import React from 'react' como algo recién agregado, sino más bien como una aceptación de la realidad actual. Aunque quizás fue involuntario y solo un efecto secundario de la interoperabilidad ESM / CJS ahora obsoleta, todavía hay miles (¿millones?) De líneas de código que lo hacen. La alternativa (solo exportar exportaciones con nombre) dice a los usuarios "publicamos una compilación de ESM en una versión menor, pero no puede usarla sin cambiar todo su código", lo que para mí es más confuso para los usuarios que ver "exportación predeterminada eliminada" en las notas de la versión principal.

Tengo curiosidad: ¿cómo se puede agregar ESM con una exportación predeterminada de una manera compatible con versiones anteriores? Esto ha surgido antes (por ejemplo, https://github.com/facebook/react/pull/18187 que también se vincula a problemas relacionados). El problema es con la interoperabilidad del paquete web CJS <-> ESM, donde si tiene el código CJS haciendo require('react') paquete web devolverá en presencia de un ESM react con una exportación predeterminada, es un objeto con default propiedad (lo que significa que ahora requiere require('react').default ) independientemente del CJS react . Pero tal vez si también exporta named, ¿no será un problema? Creo que @TrySound se ha encontrado con este tipo de problemas en otros paquetes antes.

Pero tal vez si también exporta named, ¿no será un problema?

Sí, este es el enfoque en el que estoy pensando. Vea las últimas 40 líneas de https://unpkg.com/browse/@esm-bundle/react @ 16.13.1 / esm / react.development.js: es una versión no oficial de React que hace exactamente esto y es utilizada por varios organizaciones como un reemplazo directo del React oficial. Sin cambios importantes.

Pero tal vez si también exporta named, ¿no será un problema?

Sí, este es el enfoque en el que estoy pensando. Vea las últimas 40 líneas de https://unpkg.com/browse/@esm-bundle/react @ 16.13.1 / esm / react.development.js: es una versión no oficial de React que hace exactamente esto y es utilizada por varios organizaciones como un reemplazo directo del React oficial. Sin cambios importantes.

Pero, ¿lo está consumiendo desde el código CJS o ESM? Porque son los problemas de interoperabilidad de CJS <-> ESM los que pueden resultar muy sorprendentes.

@gaearon para ser claro; tiene sentido no tener una exportación predeterminada en el contenedor de ESM que propongo; cualquiera que haga un ESM nativo haría import * as React from 'react' para solucionarlo. Sin embargo, es justo que cualquiera que haga eso ahora lo vea como un cambio importante el no tener repentinamente la exportación predeterminada, por lo que tendría que esperar hasta la v17 si no desea agregar la predeterminada ahora.

Pero, ¿lo está consumiendo desde el código CJS o ESM? Porque son los problemas de interoperabilidad de CJS <-> ESM los que pueden resultar muy sorprendentes.

Lo importé con éxito en el paquete web desde archivos CJS y ESM.

cualquiera que haga un ESM nativo haría una importación * como Reaccionar de 'reaccionar' para solucionarlo. Sin embargo, es justo que cualquiera que haga eso ahora lo vea como un cambio importante el no tener repentinamente la exportación predeterminada, por lo que tendría que esperar hasta la v17 si no desea agregar la predeterminada ahora.

Acordado. Si comienza desde cero, no sería necesario tener la exportación predeterminada. Sin embargo, sin agregar una exportación predeterminada, no es posible implementar la Fase 2 sin que sea un cambio importante. Personalmente, estaría bien con hacer la Fase 1 en React 16 y las Fases 2-4 en React 17+, aunque mi preferencia es hacer la Fase 1, 2 y tal vez incluso 3 (con la ayuda de la herramienta de verificación de exportaciones de @ljharb ) en React 16 sin cambios importantes. La razón es que la fase 2 es la más importante en la que la mayoría de los usuarios comienzan a usar el paquete ESM, mientras que la fase 1 es principalmente para los primeros usuarios / entusiastas.

Continuando con esta propuesta en adelante. Esto parece ser una propuesta para un cambio a un paquete dual con fuente completa de CJS y ESM que se ocupa del peligro del paquete dual al aislar el estado en otro lugar ( enfoque 2 ). A diferencia de usar un contenedor ESM como mi RFC anterior ( enfoque 1 ).

Suponiendo eso, tengo algunas notas de cosas que aún no se han mencionado.

  • No puede tener un paquete Node.js donde los archivos ESM y CommonJS usen .js . Si configura "type": "module" entonces todos los archivos CommonJS deben usar la extensión de archivo .cjs lugar.
  • El peligro de paquete dual estatal e igualitario no es el único problema de tener CJS y ESM. Tener potencialmente 2 versiones del mismo paquete cargadas también aumenta la huella de memoria de React en esos casos, y React no es una biblioteca pequeña. Esto no es un factor decisivo, pero vale la pena tenerlo en cuenta.
  • Veo potencial para un peligro de paquete dual además del estado interno de React. Por ejemplo, si la implementación de la clase Component no es parte del código CJS donde compartimos el estado y, en cambio, es parte de los paquetes CJS / ESM, entonces existe el riesgo de que se instanceof Component comprobaciones de

No puede tener un paquete Node.js donde los archivos ESM y CommonJS usen .js. Si establece "tipo": "módulo", entonces todos los archivos CommonJS deben usar la extensión de archivo .cjs en su lugar.

Esto es cierto para NodeJS (pero no para los paquetes), por lo que los archivos en el directorio cjs tendrían que terminar con .cjs .

Tener potencialmente 2 versiones del mismo paquete cargadas también aumenta la huella de memoria de React en esos casos

Veo cómo esto aumenta el tamaño del tarball publicado en npm y, por lo tanto, el tamaño general en el disco. Pero no veo cómo esto afecta la memoria. Hasta donde yo sé, los paquetes y NodeJS no traen código a la memoria que no se haya cargado a través de import / require() . ¿Podría aclarar cómo cambiaría la huella de memoria?

Por ejemplo, si la implementación de la clase Component no es parte del código CJS donde compartimos el estado y, en cambio, es parte de los paquetes CJS / ESM, entonces existe el riesgo de que se rompan las comprobaciones de los componentes en varias bibliotecas.

Una solución propuesta ( 1 , 2 ) es que la implementación de esm de NodeJS sea simplemente una interfaz de ESM que llame al código CJS. De esa manera, solo hay una definición de Component que se usa en Node. Sin embargo, la Fase 1 y la Fase 2 no cambiarían lo que se ejecuta en NodeJS, por lo que esto solo se aplicaría a la Fase 3.

Como nosotros (paquete web) recientemente también agregamos exports soporte de campo al paquete web 5, quiero dar mis 2 centavos a este tema:

  • Este documento de trabajo en proceso tiene mucha información sobre el campo exports respecto al paquete web, pero también Node.js y en general: https://gist.github.com/sokra/e032a0f17c1721c71cfced6f14516c62
  • Estos son los puntos clave en comparación con Node.js:

    • webpack 5 también agrega condiciones development y production , que son muy útiles para reaccionar. ( process.env.NODE_ENV , aunque aún es compatible, debe evitarse para el código frontend en general, es específico de Node.js)

    • webpack (y otros paquetes) admite require("esm") , lo que permite evitar el problema del estado dual al usar siempre ESM (incluso para require() ). webpack ha introducido una condición especial para eso: module . Para esta versión de CommonJs y ESM se debe exportar la misma interfaz. Actualmente, no hay otra cosa que tenga el problema del estado dual que Node.js. No espero que veamos algo en el futuro, ya que se trata principalmente de un problema de compatibilidad con versiones anteriores.

      Para una máxima compatibilidad, recomendaría lo siguiente:

package.json

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

esm / package.json

Permite usar .js como extensión en este directorio. Alternativamente, se podría usar .mjs , pero esto podría tener efectos secundarios potenciales cuando las herramientas verifiquen la extensión. Entonces .js para estar seguro.

{
    "type": "module"
}

esm / wrapper.js

Este contenedor es necesario para Node.js para evitar el problema del estado dual.

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

cjs / index.js

Node.js lo usa cuando las condiciones development y production no son compatibles.

'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 (similar a esm / wrapper.production.min.js)

Estos contenedores son necesarios para Node.js para evitar el problema del estado dual.
Solo se usan una vez que Node.js agrega condiciones development y production .

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

index.js

Para compatibilidad con versiones anteriores.

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

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

Esto lo utilizan las herramientas que admiten el campo exports , la condición module y las condiciones 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 }

Resultados

  • paquete web 5: ./esm/index.development.js o ./esm/index.production.min.js
  • browserify: ./cjs/index.js
  • paquete web 4 desde .mjs: ./cjs/index.js
  • otros paquetes: ./esm/wrapper.js
  • Node.js (ESM): ./cjs/index.js (requerir) o ./esm/wrapper.js (importar)
  • Node.js (antiguo): ./cjs/index.js
  • Node.js (ESM + dev / prod): ./esm/wrapper.development.js o ./esm/wrapper.production.min.js para importación, ./cjs/index.development.js o ./cjs/index.production.min.js para requerir

Notas

No hay esm/index.js ya que elegir condicionalmente una versión no es posible en ESM sin importantes compensaciones.
Las herramientas solo pueden beneficiarse completamente de react ESM cuando admiten el campo exports , la condición module (debido al problema de estado dual) y production / development condiciones (debido al problema de importación condicional).

Las herramientas pueden beneficiarse parcialmente de react ESM cuando admiten el campo module o el campo exports .

import { useState } from "react" o import * as React from "react" son técnicamente ilegales siempre que react sea un módulo CommonJs.
La mayoría de las herramientas todavía lo admiten para compatibilidad con versiones anteriores, pero algunas no, por ejemplo, Node.js
Entonces, actualmente, la única forma de usar react que es válida en todas partes es: import React from "react" .
Esta forma debería seguir siendo compatible, de lo contrario, habría casos (por ejemplo, Node.js 14) en los que no hay una sintaxis válida para reaccionar ahora y reaccionar después de la adición de ESM.

Node.js rechazó la adición de una condición development / production para exports por ahora.
Eso es triste, y todavía espero lo mejor de que eventualmente lo agreguen.
Es por eso que el soporte para eso está preparado en el campo exports anterior.

@sokra gran desglose, muy útil, ¡gracias!

Una pequeña pregunta:

Node.js rechazó la adición de una condición de desarrollo / producción para las exportaciones por ahora.

¿Tengo entendido que todavía se está trabajando? https://github.com/nodejs/node/pull/33171 pero tal vez estoy entendiendo mal ese PR

[editar] el PR anterior al que me vinculé fue reemplazado por https://github.com/nodejs/node/pull/34637

[edit2] y ahora se ha fusionado en nodejs

Gracias @sokra , esas son sugerencias muy útiles.

Aquí están las opciones que veo. Parece que todas son técnicamente posibles, y que la decisión es de estrategia más que de implementación técnica:

Opción 1

Agregue export default React a una compilación de React 17 ESM y elimínelo una vez que se elimine el soporte de CJS para import React from 'react' (¿quizás en React 18?).

opcion 2

No agregue export default React y cree una compilación de React 17 ESM solo con exportaciones con nombre.

Opción 3

No publique una compilación de React 17 ESM. (😢) Espere hasta que se elimine el soporte de import React from 'react'; antes de crear una compilación de ESM.

Comparación

| | Opción 1 | Opción 2 | Opción 3 |
| - | -------- | -------- | -------- |
| Compilación de ESM sin referencia | v17 | v17 | v18 + |
| package.json "módulo" (árbol tembloroso por defecto) | v17 | v18 + | v18 + |
| package.json "tipo" / "exportaciones" (NodeJS usa ESM) | v18 + 1 | v18 + | v18 + |

  1. Es posible implementar el tipo / exportaciones de package.json de una manera totalmente compatible con versiones anteriores, en cuyo caso podría ser parte de React 17 si se elige la Opción 1.

Mi preferencia es hacia la Opción 1, como he explicado anteriormente. Sin embargo, la Opción 2 también es muy emocionante para mí. La opción 3 es, por supuesto, menos emocionante. Por lo que he recopilado en este número de github, tenemos la experiencia técnica para hacer que cualquiera de estos suceda (¡y probablemente incluso la mano de obra!).

La reacción inicial a este problema fue, ¿por qué está abierto este problema incluso después de 3 años ? Después de leer parte de él, tiene sentido por qué está tardando tanto. Mantener una biblioteca como React es una tarea enorme. Entonces 🙇🏻

Dadas las noticias recientes con React 17, he actualizado mi comentario anterior para hacer referencia a React 17 en lugar de 16 para cualquier plan futuro.

Agradecería los comentarios de las personas sobre cuál de las tres opciones anteriores se prefiere.

Creo que podemos agregar el campo exports en package.json en React 17, probablemente también podríamos exportarlo a versiones anteriores:

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

Necesitaríamos nuevos paquetes de esm, aunque eso no debería ser demasiado difícil de agregar con el resumen.

  • Los paquetes ./esm/react.development.mjs y ./esm/react.production.mjs deben estar libres de process.env.NODE_ENV cheques:

    • la condición se resuelve en el momento de la importación / agrupación mediante el campo exports .

    • process es una API de nodo, no tiene sentido en un entorno de navegador y no es compatible con _default_ por webpack 5, por ejemplo.

  • El ./esm/react.node.mjs quedaría con los cheques process.env.NODE_ENV .
  • AFAIK solo el paquete web 5 y el nodo admiten el campo exports este momento.

Creo que es bastante seguro agregar esto, ¿WDYT?

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

El "./": "./" hace seguro, sí, pero también evita cualquier encapsulación, por lo que querrá eliminarlo tan pronto como tenga un semver-major.

FWIW babel genera la nueva importación de tiempo de ejecución jsx como

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

pero si carga un módulo como ese en Node, se quejará de la falta de extensión de archivo:

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

Agregar exports debería solucionarlo 🤔

@nstepien proporcionar un mapa exports completo como lo ha mostrado en su publicación anterior no es una opción de lo que creo. Lo que implementa el nodo con respecto a la interoperabilidad de cjs y esas cosas realmente no funciona bien con el ecosistema existente. El peligro de un paquete dual es real, especialmente para paquetes como Reac que requieren una única copia de ellos.

Un mapa exports con archivos commonjs solo podría agregarse potencialmente sin romper nada, pero también tendría que hacerse con mucho cuidado y con las pruebas e2e adecuadas para esto (dado lo complejas que son las cosas para hacerlo bien)

@Andarist funciona bien, y no es diferente en el caso de react, que siempre ha tenido ese peligro y el ecosistema lo resuelve haciendo reaccionar un peer dep en todas partes. Un mapa de "exportaciones" puede funcionar bien aquí, siempre que los archivos ESM y los archivos CJS compartan el mismo estado, lo que se puede lograr escribiendo contenedores ESM simples.

Si todas las dependencias, transitivas o no, de React están bajo el control de React (que en este caso lo están) y los puntos de entrada de ESM de todas ellas simplemente están reexportando contenido de CJS, entonces sí, tal vez eso se pueda lograr en este caso particular.

Sin embargo, todavía existe todo el drama de cuál debería ser la forma real de la entrada de ESM (con nombre, predeterminado, ambos):

  • solo con nombre: no es realmente compatible con versiones anteriores porque gran parte del código está usando import React from 'react' , que también es la única forma de importar React en el nodo ahora mismo cuando se usa ESM
  • solo por defecto: no es realmente compatible con versiones anteriores porque gran parte del código está usando import * as React from 'react' , esto a menudo ha sido promovido por verificadores de tipo y otras herramientas
  • ambos: la única forma de hacerlo totalmente compatible con versiones anteriores, para que pueda funcionar con todos los estilos de carga actuales y al mezclar módulos ESM y CJS en el árbol de dependencias

Constantemente me olvido de la posibilidad de los envoltorios de ESM, ya que se sienten como una trampa, pero también porque esta técnica solo funciona si controlas todas tus dependencias y no se puede usar realmente como una estrategia universal que "simplemente funcionaría" 😢

Me temo que la única estrategia universal para proporcionar ambos es, de hecho, tener todo su código real en CJS y escribir envoltorios de ESM a su alrededor para proporcionar exportaciones con nombre. El código que no tiene estado ni se basa en la identidad se puede escribir de forma nativa en ambos, pero eso es un subconjunto, una advertencia.

jsx-runtime no tiene estado, ¿verdad? Debería ser seguro enviar ambos esm / cjs sin envoltorio.

¿Debo registrar un problema por separado para importar react/jsx-runtime en el nodo esm?

Para los paquetes frontend, un desafío adicional se une al peligro de estado dual: llamémoslo el peligro de paquete doble.

Al exportar versiones de ESM y CJS y el paquete se usa a través de require y import , ambas versiones se incluirían y duplicarían el tamaño efectivo del paquete para el paquete.

@sokra, ¿ es eso posible en la práctica? Por ejemplo, con el rollup, asumiría que dado que los módulos cjs se transforman en módulos esm, preferiría importar los módulos esm siempre que estén disponibles.

En la práctica, es posible para los paquetes que siguen la semántica de nodos, como el paquete web 5. Es importante hacer coincidir la semántica porque, de lo contrario, podría experimentar resultados diferentes cuando se ejecuta en un nodo y cuando se ejecuta un código de paquetes.

Sin embargo, el paquete web 5 maneja una condición especial "module" que se usa para deduplicar esm / cjs (los que provienen del mapa de exportaciones).

Sin embargo, si consideramos la propuesta de @ ljharb, esto no es realmente relevante porque propone que el archivo esm podría ser solo unas pocas líneas de código contenedor que simplemente reexportarían el archivo cjs.

Con una envoltura delgada de ESM, no hay peligro adicional, con o sin agrupadores, solo el normal que los departamentos de pares evitan sobre los engaños en el gráfico.

¿Fue útil esta página
0 / 5 - 0 calificaciones