React: RFC: plan para atributos / propiedades de elementos personalizados en React 18

Creado en 24 oct. 2017  ·  129Comentarios  ·  Fuente: facebook/react

Esto está destinado a la dirección # 7249. El documento describe los pros y los contras de varios enfoques que React podría usar para manejar atributos y propiedades en elementos personalizados.

TOC / Resumen

  • Fondo
  • Propuestas

    • Opción 1: solo establecer propiedades

    • Pros



      • Fácil de entender / implementar


      • Evita el conflicto con los atributos globales futuros


      • Aprovecha la "actualización" del elemento personalizado


      • Elementos personalizados tratados como cualquier otro componente de React



    • Contras



      • Posiblemente un cambio rotundo


      • Necesita una referencia para establecer el atributo


      • No está claro cómo funcionaría la representación del lado del servidor



    • Opción 2: Propiedades, si están disponibles

    • Pros



      • Cambio inquebrantable



    • Contras



      • Los desarrolladores deben comprender la heurística


      • Recurrir a los atributos puede entrar en conflicto con los globales futuros



    • Opción 3: diferenciar propiedades con un sigilo

    • Pros



      • Cambio inquebrantable en el que los desarrolladores pueden optar


      • Similar a cómo otras bibliotecas manejan atributos / propiedades


      • El sistema es explícito



    • Contras



      • Es nueva sintaxis


      • No está claro cómo funcionaría la representación del lado del servidor



    • Opción 4: agregar un objeto de atributos

    • Pros



      • El sistema es explícito


      • La extensión de la sintaxis también puede resolver problemas con el manejo de eventos.



    • Contras



      • Es nueva sintaxis


      • Puede ser un cambio rotundo


      • Puede ser un cambio mayor que cualquiera de las propuestas anteriores.



    • Opción 5: una API para consumir elementos personalizados

    • Pros



      • El sistema es explícito


      • Cambio inquebrantable


      • Idiomático para reaccionar



    • Contras



      • Podría ser mucho trabajo para un componente complejo


      • Puede hinchar el tamaño del paquete


      • La configuración debe seguir el ritmo del componente



Fondo

Cuando React intenta pasar datos a un elemento personalizado, siempre lo hace usando atributos HTML.

<x-foo bar={baz}> // same as setAttribute('bar', baz)

Debido a que los atributos deben serializarse en cadenas, este enfoque crea problemas cuando los datos que se pasan son un objeto o una matriz. En ese escenario, terminamos con algo como:

<x-foo bar="[object Object]">

La solución para esto es usar ref para configurar manualmente la propiedad.

<x-foo ref={el => el.bar = baz}>

Esta solución se siente un poco innecesaria ya que la mayoría de los elementos personalizados que se envían hoy están escritos con bibliotecas que generan automáticamente propiedades de JavaScript que respaldan todos sus atributos expuestos. Y se alienta a cualquier persona que cree a mano un elemento personalizado de vainilla

Este documento describe algunas propuestas sobre cómo se podría actualizar React para que esto suceda.

Propuestas

Opción 1: solo establecer propiedades

En lugar de intentar decidir si se debe establecer una propiedad o atributo, React siempre podría establecer propiedades en elementos personalizados. React NO verificaría de antemano si la propiedad existe en el elemento.

Ejemplo:

<x-foo bar={baz}>

El código anterior podría resultar en React estableciendo el .bar propiedad del x-foo elemento igual al valor de baz .

Para los nombres de propiedad camelCased, React podría usar el mismo estilo que usa hoy para propiedades como tabIndex .

<x-foo squidInk={pasta}> // sets .squidInk = pasta

Pros

Fácil de entender / implementar

Este modelo es simple, explícito y encaja con la "API centrada en JavaScript para el DOM" de React.

Cualquier elemento creado con bibliotecas como Polymer o Skate generará automáticamente propiedades para respaldar sus atributos expuestos. Todos estos elementos deberían "simplemente funcionar" con el enfoque anterior. Se recomienda a los desarrolladores que creen componentes vanilla a mano que respalden atributos con propiedades, ya que reflejan cuán modernos (es decir, no extraños como <input> ) elementos HTML5 ( <video> , <audio> , etc.) se han aplicado.

Evita el conflicto con los atributos globales futuros

Cuando React establece un atributo en un elemento personalizado, siempre existe el riesgo de que una versión futura de HTML envíe un atributo con un nombre similar y rompa cosas. Esta preocupación se discutió con los autores de las especificaciones, pero no existe una solución clara al problema. Evitar los atributos por completo (excepto cuando un desarrollador establece explícitamente uno usando ref ) puede evitar este problema hasta que los navegadores encuentren una solución mejor.

Aprovecha la "actualización" del elemento personalizado

Los elementos personalizados se pueden actualizar perezosamente en la página y algunos patrones PRPL se basan en esta técnica. Durante el proceso de actualización, un elemento personalizado puede acceder a las propiedades que le ha pasado React, incluso si esas propiedades se establecieron antes de que se cargara la definición, y usarlas para representar el estado inicial.

Elementos personalizados tratados como cualquier otro componente de React

Cuando los componentes de React se pasan datos entre sí, ya usan propiedades. Esto solo haría que los elementos personalizados se comporten de la misma manera.

Contras

Posiblemente un cambio rotundo

Si un desarrollador ha creado manualmente elementos personalizados vanilla que solo tienen una API de atributos, entonces deberá actualizar su código o su aplicación fallará. La solución sería usar ref para establecer el atributo (explicado a continuación).

Necesita una referencia para establecer el atributo

Al cambiar el comportamiento para que se prefieran las propiedades, significa que los desarrolladores necesitarán usar ref para establecer explícitamente un atributo en un elemento personalizado.

<custom-element ref={el => el.setAttribute('my-attr', val)} />

Esto es solo una inversión del comportamiento actual en el que los desarrolladores necesitan ref para establecer una propiedad. Dado que los desarrolladores rara vez deberían necesitar establecer atributos en elementos personalizados, esto parece una compensación razonable.

No está claro cómo funcionaría la representación del lado del servidor

No está claro cómo se correlacionaría este modelo con los elementos personalizados de renderizado del lado del servidor. React podría asumir que las propiedades se asignan a atributos con nombres similares e intentar establecerlos en el servidor, pero esto está lejos de ser a prueba de balas y posiblemente requeriría una heurística para cosas como propiedades camelCased -> atributos en guión.

Opción 2: Propiedades, si están disponibles

En tiempo de ejecución, React podría intentar detectar si una propiedad está presente en un elemento personalizado. Si la propiedad está presente, React la usará; de lo contrario, recurrirá a la configuración de un atributo. Este es el modelo que usa Preact para tratar con elementos personalizados.

Implementación de pseudocódigo:

if (propName in element) {
  element[propName] = value;
} else {
  element.setAttribute(propName.toLowerCase(), value);
}

Posibles pasos:

  • Si un elemento tiene una propiedad definida, React la usará.

  • Si un elemento tiene una propiedad indefinida y React está tratando de pasarle datos primitivos (cadena / número / booleano), usará un atributo.

    • Alternativa: advertir y no configurar.
  • Si un elemento tiene una propiedad indefinida y React está tratando de pasarle un objeto / matriz, lo establecerá como una propiedad. Esto se debe a que some-attr = "[object Object]” no es útil.

    • Alternativa: advertir y no configurar.
  • Si el elemento se representa en el servidor y React intenta pasarle una cadena / número / booleano, usará un atributo.

  • Si el elemento se representa en el servidor y React intenta pasarle un objeto / matriz, no hará nada.

Pros

Cambio inquebrantable

Es posible crear un elemento personalizado que solo use atributos como interfaz. Este estilo de autor NO se recomienda, pero puede suceder independientemente. Si el autor de un elemento personalizado se basa en este comportamiento, entonces este cambio no sería rotundo para él.

Contras

Los desarrolladores deben comprender la heurística

Los desarrolladores pueden confundirse cuando React establece un atributo en lugar de una propiedad, dependiendo de cómo hayan elegido cargar su elemento.

Recurrir a los atributos puede entrar en conflicto con los globales futuros

Sebastian expresó su preocupación de que usar in para verificar la existencia de una propiedad en un elemento personalizado podría detectar accidentalmente una propiedad en la superclase (HTMLElement).

También hay otros posibles conflictos con los atributos globales discutidos anteriormente en este documento.

Opción 3: diferenciar propiedades con un sigilo

React podría continuar estableciendo atributos en elementos personalizados, pero proporcionar un sello que los desarrolladores podrían usar para establecer propiedades explícitamente en su lugar. Esto es similar al enfoque utilizado por Glimmer.js .

Ejemplo de Glimmer:

<custom-img @src="corgi.jpg" @hiResSrc="[email protected]" width="100%">

En el ejemplo anterior, @ sigil indica que src y hiResSrc deben pasar datos al elemento personalizado usando propiedades, y width deben serializarse en una cadena de atributos.

Debido a que los componentes de React ya se pasan datos entre sí usando propiedades, no habría necesidad de que usen el sigilo (aunque funcionaría si lo hicieran, simplemente sería redundante). En cambio, se usaría principalmente como una instrucción explícita para pasar datos a un elemento personalizado usando propiedades de JavaScript.

h / t a @developit de Preact por sugerir este enfoque :)

Pros

Cambio inquebrantable en el que los desarrolladores pueden optar

Todas las aplicaciones de elementos personalizados React + preexistentes continuarían funcionando exactamente como lo han hecho. Los desarrolladores podían elegir si querían actualizar su código para usar el nuevo estilo de sigilo.

Similar a cómo otras bibliotecas manejan atributos / propiedades

Al igual que Glimmer, tanto Angular como Vue usan modificadores para diferenciar entre atributos y propiedades.

Ejemplo de Vue:

<!-- Vue will serialize `foo` to an attribute string, and set `squid` using a JavaScript property -->
<custom-element :foo="bar” :squid.prop=”ink”>

Ejemplo angular:

<!-- Angular will serialize `foo` to an attribute string, and set `squid` using a JavaScript property -->
<custom-element [attr.foo]="bar” [squid]=”ink”>

El sistema es explícito

Los desarrolladores pueden decirle a React exactamente lo que quieren en lugar de confiar en una heurística como el enfoque de propiedades si están disponibles .

Contras

Es nueva sintaxis

Los desarrolladores deben aprender a usarlo y debe probarse a fondo para asegurarse de que sea compatible con versiones anteriores.

No está claro cómo funcionaría la representación del lado del servidor

¿Debería el sigilo cambiar a un atributo con un nombre similar?

Opción 4: agregar un objeto de atributos

React podría agregar una sintaxis adicional que permita a los autores pasar datos explícitamente como atributos. Si los desarrolladores no utilizan este objeto de atributos, sus datos se pasarán utilizando propiedades de JavaScript.

Ejemplo:

const bar = 'baz';
const hello = 'World';
const width = '100%';
const ReactElement = <Test
  foo={bar} // uses JavaScript property
  attrs={{ hello, width }} // serialized to attributes
/>;

Esta idea fue propuesta originalmente por @treshugart , autor de Skate.js, y está implementada en la biblioteca val .

Pros

El sistema es explícito

Los desarrolladores pueden decirle a React exactamente lo que quieren en lugar de confiar en una heurística como el enfoque de propiedades si están disponibles .

La extensión de la sintaxis también puede resolver problemas con el manejo de eventos.

Nota: Esto está fuera del alcance de este documento, pero tal vez valga la pena mencionarlo :)

El número 7901 solicita que React omita su sistema de eventos sintéticos cuando se agregan controladores de eventos declarativos a elementos personalizados. Debido a que los nombres de eventos de elementos personalizados son cadenas arbitrarias, significa que pueden escribirse en mayúscula de cualquier manera. Eludir el sistema de eventos sintéticos de hoy también significará la necesidad de crear una heurística para mapear nombres de eventos desde JSX a addEventListener .

// should this listen for: 'foobar', 'FooBar', or 'fooBar'?
onFooBar={handleFooBar}

Sin embargo, si la sintaxis se amplía para permitir atributos, también podría ampliarse para permitir eventos también:

const bar = 'baz';
const hello = 'World';
const SquidChanged = e => console.log('yo');
const ReactElement = <Test
  foo={bar}
  attrs={{ hello }}
  events={{ SquidChanged}} // addEventListener('SquidChanged', …)
/>;

En este modelo, el nombre de la variable se utiliza como nombre del evento. No se necesita heurística.

Contras

Es nueva sintaxis

Los desarrolladores deben aprender a usarlo y debe probarse a fondo para asegurarse de que sea compatible con versiones anteriores.

Puede ser un cambio rotundo

Si algún componente ya depende de propiedades denominadas attrs o events , podría romperlos.

Puede ser un cambio mayor que cualquiera de las propuestas anteriores.

Para React 17 puede ser más fácil hacer un cambio incremental (como una de las propuestas anteriores) y posicionar esta propuesta como algo a tener en cuenta para una refactorización posterior más grande.

Opción 5: una API para consumir elementos personalizados

Esta propuesta fue ofrecida por @sophiebits y @gaearon del equipo React

React podría crear una nueva API para consumir elementos personalizados que mapea el comportamiento del elemento con un objeto de configuración.

Ejemplo de pseudocódigo:

const XFoo = ReactDOM.createCustomElementType({
  element: ‘x-foo’,
  ‘my-attr’: // something that tells React what to do with it
  someRichDataProp: // something that tells React what to do with it
});

El código anterior devuelve un componente de proxy, XFoo que sabe cómo pasar datos a un elemento personalizado según la configuración que proporciones. Usaría este componente de proxy en su aplicación en lugar de usar el elemento personalizado directamente.

Uso de ejemplo:

<XFoo someRichDataProp={...} />

Pros

El sistema es explícito

Los desarrolladores pueden decirle a React el comportamiento exacto que desean.

Cambio inquebrantable

Los desarrolladores pueden optar por usar el objeto o continuar usando el sistema actual.

Idiomático para reaccionar

Este cambio no requiere una nueva sintaxis JSX y se parece más a otras API en React. Por ejemplo, PropTypes (aunque se está moviendo a su propio paquete) tiene un enfoque algo similar.

Contras

Podría ser mucho trabajo para un componente complejo

El elemento de entrada de

Puede hinchar el tamaño del paquete

En relación con el punto anterior, cada clase de elemento personalizado ahora incurre en el costo de su definición + el tamaño de su objeto de configuración.

Nota: No estoy 100% seguro de que esto sea cierto.

La configuración debe seguir el ritmo del componente

Cada vez que el componente realiza una revisión menor de la versión que agrega una nueva propiedad, la configuración también deberá actualizarse. Eso no es difícil, pero agrega mantenimiento. Tal vez, si las configuraciones se generan desde la fuente, esto sea menos una carga, pero eso puede significar la necesidad de crear una nueva herramienta para generar configuraciones para cada biblioteca de componentes web.

cc @sebmarkbage @gaearon @developit @treshugart @justinfagnani

DOM Discussion

Comentario más útil

Hola amigos, mientras tanto, mientras esperamos, creé una calza para envolver su componente web en React https://www.npmjs.com/package/reactify-wc

import React from "react";
import reactifyWc from "reactify-wc";

// Import your web component. This one defines a tag called 'vaadin-button'
import "@vaadin/vaadin-button";

const onClick = () => console.log('hello world');

const VaadinButton = reactifyWc("vaadin-button");

export const MyReactComponent = () => (
  <>
    <h1>Hello world</h1>
    <VaadinButton onClick={onClick}>
      Click me!
    </VaadinButton>
  </>
)

Espero que esto te resulte útil

(Esta es mi primera incursión en OSS, y una de las primeras fuentes abiertas de algo fuera de mi oficina. Las críticas constructivas son más que bienvenidas 😄)

Todos 129 comentarios

Disculpas por la lectura larga, pero quería asegurarme de estar explorando a fondo cada opción. No quiero sesgar demasiado las cosas con mi propia opinión, pero si estuviera en condiciones de elegir, creo que optaría por la opción 3.

La opción 3 es compatible con versiones anteriores, declarativa y explícita. No es necesario mantener una heurística alternativa, y otras bibliotecas ya proporcionan sigilos / modificadores similares.

Disculpas por la lectura larga, pero quería asegurarme de estar explorando a fondo cada opción. No quiero sesgar demasiado las cosas con mi propia opinión, pero si estuviera en condiciones de elegir, creo que optaría por la opción 3.
La opción 3 es compatible con versiones anteriores, declarativa y explícita. No es necesario mantener una heurística alternativa, y otras bibliotecas ya proporcionan sigilos / modificadores similares.

Estoy entre la opción 2 y la opción 3, creo que React ha manejado muy bien el comportamiento y los cambios de API en el pasado. La introducción de advertencias y enlaces a documentos puede ser útil para ayudar a los desarrolladores a comprender lo que está sucediendo bajo el capó.

La opción 3 parece atractiva debido a su naturaleza declarativa, mientras que al leer el código JSX, los nuevos desarrolladores que vienen sabrán de inmediato qué hará React al renderizar el elemento.

Comentarios sobre la opción 2

Los desarrolladores pueden confundirse cuando React establece un atributo en lugar de una propiedad, dependiendo de cómo hayan elegido cargar su elemento.

¿Los consumidores de un elemento personalizado deben comprender esta distinción? ¿O es eso solo importante para el autor del elemento personalizado? Parece que el autor del elemento necesitará manejar atributos para cualquier cosa que se use en HTML (ya que esa es la única forma en que los datos se pasan del uso de HTML) y propiedades si quieren admitir valores complejos o propiedades get / set de DOM. Incluso es posible que un autor pueda tener algo implementado inicialmente como un atributo y luego agregar una propiedad con el mismo nombre para admitir tipos de datos más flexibles y aún así respaldar la propiedad con un valor almacenado en los atributos.

Las colisiones de nombres con los atributos y propiedades futuros de HTMLElement parecen ser una debilidad en los estándares de los componentes web en general, ya que pueden dar lugar a errores independientemente del enfoque de vinculación.

Si un elemento tiene una propiedad indefinida y React está tratando de pasarle un objeto / matriz, lo establecerá como una propiedad. Esto se debe a que some-attr = "[object Object]” no es útil.

Parece confuso vincular de manera diferente según el valor. Si el autor del elemento no ha especificado un getter / setter de propiedad para manejar el valor, la configuración de la propiedad haría que el elemento se comporte como si el valor nunca se hubiera especificado, lo que podría ser más difícil de depurar.

Comentarios sobre la opción 3

Otra desventaja potencial con la opción 3 es que requiere que el consumidor del elemento personalizado sepa si el elemento ha implementado algo como una propiedad o como un atributo. Si está utilizando una combinación de componentes de React y elementos personalizados, podría ser confuso configurar los accesorios de React usando una sintaxis y propiedades de elementos personalizados usando una sintaxis diferente.

¿Los consumidores de un elemento personalizado deben comprender esta distinción? ¿O es eso solo importante para el autor del elemento personalizado?

Dudo que en realidad sea un gran problema porque, como señaló, el autor del elemento debe definir un atributo y una propiedad para el valor subyacente y aceptar datos de ambos. También agregaría que deben mantener el atributo y la propiedad sincronizados (por lo tanto, configurar uno establece el otro).

Las colisiones de nombres con los atributos y propiedades futuros de HTMLElement parecen ser una debilidad en los estándares de los componentes web en general, ya que pueden dar lugar a errores independientemente del enfoque de vinculación.

Estoy de acuerdo, pero no estoy seguro de si esto es algo que React necesita intentar solucionar en su biblioteca. Se siente como un problema que debe resolverse como parte de la especificación de elementos personalizados. Puedo ver si podemos discutirlo como parte de la próxima reunión de estándares TPAC.

Debo agregar, para las propiedades esto no es _ tan_ malo porque la propiedad definida por el elemento sombreará la propiedad futura agregada a HTMLElement. Entonces, si estuviera pasando datos a un elemento personalizado como una propiedad js, continuaría funcionando. El problema principal parece estar relacionado con los atributos, ya que son globales.

Parece confuso vincular de manera diferente según el valor. Si el autor del elemento no ha especificado un getter / setter de propiedad para manejar el valor, la configuración de la propiedad haría que el elemento se comporte como si el valor nunca se hubiera especificado, lo que podría ser más difícil de depurar.

En el caso de que un elemento personalizado se cargue de forma diferida y se "actualice", inicialmente tendrá propiedades indefinidas. Esto aborda ese caso de uso asegurándose de que esos elementos aún reciban sus datos y puedan usarlos después de la actualización.

Es cierto que si el autor no define un getter / setter para un valor, esto no sería muy útil. Pero tampoco es útil tener un my-attr=[object Object] . Y dado que no sabe si la propiedad es realmente indefinida o si la definición solo se carga de forma diferida, parece más seguro establecer la propiedad.

Otra desventaja potencial con la opción 3 es que requiere que el consumidor del elemento personalizado sepa si el elemento ha implementado algo como una propiedad o como un atributo.

Creo que hoy estás esencialmente en el mismo barco porque no hay nada que obligue al autor de un elemento personalizado a definir un atributo en lugar de una propiedad. Entonces, podría tener un elemento con una API de solo propiedades que no recibiría ningún dato del sistema actual de React y necesitaría saber cómo usar ref para establecer directamente las propiedades js.

Debido a que los elementos personalizados están pensados ​​como primitivos, no hay nada que obligue a crear los atributos y propiedades correspondientes. Pero estamos haciendo un gran esfuerzo para alentarlo a hacerlo como una mejor práctica, y todas las bibliotecas que conozco hoy crean propiedades de respaldo para sus atributos.

[editar]

Como dijiste en tu punto anterior:

Parece que el autor del elemento necesitará manejar atributos para cualquier cosa que se use en HTML (ya que esa es la única forma en que los datos se pasan del uso de HTML) y propiedades si quieren admitir valores complejos o propiedades get / set de DOM.

Debido a que nunca se sabe cómo un usuario intentará pasar datos a su elemento, terminará necesitando una correspondencia atributo-propiedad de todos modos. Me imagino que si se enviara la opción 3, la mayoría de la gente simplemente uniría todo usando el sigilo @ porque sería más fácil. Así es como trabajo con elementos personalizados en Vue hoy, ya que exponen un modificador .prop .

requiere que el consumidor del elemento personalizado sepa si el elemento ha implementado algo como una propiedad o como un atributo

Eso no es algo que React deba preocupar como Rob dijo en mi opinión, es responsabilidad del autor del elemento personalizado informar al usuario cómo funciona el elemento.

Y en realidad es la forma en que necesitamos hacerlo hoy, por ejemplo, piense en el elemento <video> , digamos que necesita silenciarlo o cambiar la hora actual dentro de un componente.

muted funciona como un atributo booleano

render() {
  return (
    <div className="video--wrapper">
      <video muted={ this.state.muted } />
    </div>
  );
}

Por el momento, debe crear un ref apunte al elemento de video y cambiar la propiedad.

render() {
  return (
    <div className="video--wrapper">
      <video ref={ el => this.video = el } muted={ this.state.muted } />
    </div>
  );
}

Luego, cree un controlador de eventos, un método de instancia y configure manualmente la propiedad en el elemento DOM.

onCurrenTimeChange(e) {
  this.video.currentTime = e.value;
}

Si lo piensa, rompe un poco el modelo declarativo que el propio React impone con su API y la capa abstracta JSX, ya que currentTime es claramente un estado en el componente de envoltura, con el enlace de propiedad todavía necesitaríamos el controlador de eventos, pero el El modelo de abstracción JSX sería más declarativo y las referencias no serían necesarias solo para esto:

render() {
  return (
    <div className="video--wrapper">
      <video muted={ this.state.muted } @currentTime={ this.state.currentTime } />
    </div>
  );
}

Mi punto es que ya sea que confíe en elementos nativos o personalizados, aún necesita conocerlos en función de la documentación, la diferencia de que en el segundo caso debería provenir del autor del elemento personalizado.

@cjorasch mis dos centavos :)

Si estuviéramos diseñando esto desde cero, sin necesidad de considerar la compatibilidad con versiones anteriores, creo que la opción 1 sería la más idiomática según la "API centrada en JavaScript para el DOM" de React.

Con respecto a la representación del lado del servidor, ¿podría resolverse ese problema proporcionando una API para el código de la aplicación para informar a React sobre cómo asignar propiedades de elementos personalizados a atributos? ¿Similar a los mapas que React ya mantiene para los atributos definidos por la plataforma? Esta API solo debería invocarse una vez por nombre de elemento personalizado (no para cada instancia), y solo para propiedades que no sigan una correspondencia directa 1: 1 con su atributo, lo que con suerte debería ser relativamente raro.

Sin embargo, si nos preocupa que esto sea un cambio demasiado importante, creo que la opción 3 también es bastante atractiva. Si el sigilo significa una propiedad, sugeriría ".", Ya que ese ya es el descriptor de acceso de propiedad de JavaScript. Sin embargo, creo que es desafortunado que cada instancia en la que se usa un elemento personalizado en una aplicación sea responsable de saber cuándo usar un atributo y cuándo usar una propiedad. Lo que prefiero de la opción 1 es que incluso si se necesita una propiedad para asignar un atributo, ese código de asignación se puede aislar de todos los usos de JSX.

En el caso de que un elemento personalizado se cargue de forma diferida y se "actualice", inicialmente tendrá propiedades indefinidas. Esto aborda ese caso de uso asegurándose de que esos elementos aún reciban sus datos y puedan usarlos después de la actualización.

Quizás no entiendo el proceso de actualización. Los elementos normalmente tendrían propiedades definidas como captadores / definidores en el prototipo de clase. Marcar propName in element devolvería verdadero debido a la existencia del getter / setter incluso si el valor de la propiedad aún no está definido. Durante la actualización, ¿los valores de propiedad se establecen en alguna instancia temporal y luego se copian en la instancia real una vez que se completa la carga diferida?

La actualización es el proceso mediante el cual el elemento personalizado recibe su clase. Antes de eso, no es una instancia de esa clase, por lo que los captadores / definidores de propiedades no están disponibles.

@jeremenichelli

muted funciona como un atributo booleano

recién verificado y también tiene una propiedad correspondiente, aunque no parece estar documentado en MDN: P

Por el momento, debe crear una referencia que apunte al elemento de video y cambiar la propiedad.

Sí, de vez en cuando encontrará API solo de propiedades en elementos HTML modernos. currentTime actualiza con una frecuencia alta, por lo que no tendría sentido reflejarlo en un atributo HTML.

Mi punto es que si confía en elementos nativos o personalizados, aún necesita conocerlos en función de la documentación

Sí, lamentablemente, no existe una regla única de atributos / propiedades para todos. Pero creo que, en términos generales, puede apoyarse en gran medida en las propiedades y proporcionar sintaxis para que los desarrolladores puedan usar atributos en casos especiales.

@robdodson yeap, yo también sabía sobre la propiedad silenciada 😄 Acabo de usar estos dos para demostrar que ya _en la naturaleza_ no hay una regla única para todos como mencionaste.

Tendremos que confiar en la documentación de elementos nativos y personalizados, por lo que es algo que no me importaría tomar esta decisión.

Sin embargo, mientras escribía el último fragmento de código, me gustó un poco el enlace de propiedad 💟

@effulgentsia

Sin embargo, creo que es desafortunado que cada instancia en la que se usa un elemento personalizado en una aplicación sea responsable de saber cuándo usar un atributo y cuándo usar una propiedad.

Sin embargo, creo que este ya es el caso hoy. Dado que las principales bibliotecas de elementos personalizados (polímero, patín, ¿posiblemente otros?) Crean automáticamente propiedades de respaldo para todos los atributos expuestos, los desarrolladores podrían usar el sigilo para cada propiedad en un elemento personalizado. Probablemente sería raro que tuvieran que cambiar para usar un atributo.

@cjorasch

RE: actualización. Como mencionó @effulgentsia , es posible tener un elemento personalizado en la página pero cargar su definición en un momento posterior. <x-foo> inicialmente será una instancia de HTMLElement y cuando cargo su definición más adelante, se "actualiza" y se convierte en una instancia de la clase XFoo . En este punto, se ejecutan todas las devoluciones de llamada del ciclo de vida. Usamos esta técnica en el proyecto Polymer Starter Kit. Algo así:

<app-router>
  <my-view1></my-view1>
  <my-view2></my-view2>
</app-router>

En el ejemplo anterior, no cargaremos la definición de my-view2 hasta que el enrutador cambie a ella.

Es completamente posible establecer una propiedad en el elemento antes de que se actualice, y una vez que se carga la definición, el elemento puede capturar esos datos durante una de sus devoluciones de llamada del ciclo de vida.

los desarrolladores podrían usar el sigilo para cada propiedad en un elemento personalizado

Si los desarrolladores comenzaran a hacer eso, ¿cómo diferenciaría eso el uso de una propiedad porque "puedes" de usar una propiedad porque "debes"? ¿Y no es esa una diferenciación necesaria para la renderización del lado del servidor?

Si los desarrolladores comenzaran a hacer eso, ¿cómo diferenciaría eso el uso de una propiedad porque "puedes" de usar una propiedad porque "debes"?

Lo siento, tal vez lo expresé mal. Quise decir que los desarrolladores probablemente usarían el sigilo porque daría el resultado más consistente. Puede usarlo para pasar datos primitivos o datos enriquecidos como objetos y matrices y siempre funcionará. Creo que generalmente se prefiere trabajar con propiedades en tiempo de ejecución a trabajar con atributos, ya que los atributos tienden a usarse más para la configuración inicial.

¿Y no es esa una diferenciación necesaria para la renderización del lado del servidor?

Podría darse el caso de que en el servidor el sigilo recurriera a la configuración de un atributo.

Podría darse el caso de que en el servidor el sigilo recurriera a la configuración de un atributo.

No creo que eso funcione si la razón del sigilo es que es una propiedad que no existe como atributo, como el tiempo actual del video.

diferenciar el uso de una propiedad porque "puede" de usar una propiedad porque "debe"

Creo que esta diferenciación es importante, porque existen razones completamente diferentes para elegir usar un atributo o propiedad como una optimización (por ejemplo, SSR que prefiere atributos frente a la representación del lado del cliente que prefiere propiedades) frente a algo que existe ya sea solo como un atributo o solo una propiedad.

Con respecto a la representación del lado del servidor, ¿podría resolverse ese problema proporcionando una API para el código de la aplicación para informar a React sobre cómo asignar propiedades de elementos personalizados a atributos?

Para ser más específico, sugiero algo como esto:

ReactDOM.defineCustomElementProp(elementName, propName, domPropertyName, htmlAttributeName, attributeSerializer)

Ejemplos:

// 'muted' can be set as either a property or an attribute.
ReactDOM.defineCustomElementProp('x-foo', 'muted', 'muted', 'muted')

// 'currentTime' can only be set as a property.
ReactDOM.defineCustomElementProp('x-foo', 'currentTime', 'currentTime', null)

// 'my-attribute' can only be set as an attribute.
ReactDOM.defineCustomElementProp('x-foo', 'my-attribute', null, 'my-attribute')

// 'richData' can be set as either a property or an attribute.
// When setting as an attribute, set it as a JSON string rather than "[object Object]".
ReactDOM.defineCustomElementProp('x-foo', 'richData', 'richData', 'richdata', JSON.stringify)

Para algo que solo puede ser una propiedad (donde htmlAttributeName es nulo), SSR omitiría renderizarlo y luego lo hidrataría en el cliente.

Para algo que solo puede ser un atributo (donde domPropertyName es nulo), React invocaría setAttribute() como actualmente en v16.

Para algo que puede ser ambos, React podría elegir la estrategia más óptima. Quizás eso signifique establecer siempre como una propiedad en el lado del cliente, pero como un atributo del lado del servidor. Quizás signifique establecerlo como un atributo al crear inicialmente el elemento, pero establecerlo como una propiedad cuando luego se parchee desde vdom. Quizás solo signifique establecer como atributo cuando el valor es un tipo primitivo. Idealmente, React debería poder cambiar la estrategia siempre que lo desee como un detalle de implementación interna.

Cuando React encuentra un accesorio para el que no se ha llamado defineCustomElementProp() y que no está definido por la especificación HTML como una propiedad o atributo global, entonces React puede implementar alguna lógica predeterminada. Por ejemplo, quizás:

  • En la versión 17, mantenga BC con v16 y configúrelo como un atributo.
  • En la versión 18, asuma que puede ser cualquiera y siga la estrategia más óptima para eso.

Pero en cualquier caso, al mantener esta API separada, los objetos JSX y props se mantienen limpios y dentro de un solo espacio de nombres, al igual que lo están para los componentes React y los elementos HTML no personalizados.

Perdón por los comentarios excesivos, pero pensé en otro beneficio de mi propuesta anterior que me gustaría compartir:

Esas llamadas ReactDOM.defineCustomElementProp() podrían proporcionarse en un archivo JS mantenido por el autor del elemento personalizado (en el mismo repositorio donde se mantiene / distribuye el elemento personalizado). No sería necesario para elementos personalizados con una estricta correspondencia 1: 1 de propiedad / atributo, que según la declaración de antecedentes de este problema es la recomendación y el caso mayoritario de todos modos. Por lo tanto, solo los autores de elementos personalizados que no sigan esta recomendación deberían proporcionar el archivo de integración de React. Si el autor no lo proporciona (por ejemplo, porque el autor del elemento personalizado no se preocupa por React), entonces la comunidad de personas que usan ese elemento personalizado dentro de las aplicaciones React podría autoorganizar un repositorio central para albergar ese archivo de integración.

Creo que la posibilidad de tal centralización es preferible a una solución que requiere que cada usuario del elemento personalizado siempre tenga que ser explícito con un sigilo.

La opción 3 sería mi preferida, pero eso es un gran cambio radical ... ¿Qué pasa con la inversa? ¿Los atributos tienen un prefijo, no accesorios?

Sigils en React, no sé cómo me siento al respecto. La especificación JSX debe considerarse universal, no demasiado dependiente ni dependiente de las especificaciones del navegador, especialmente no de las irregularidades debido a la compatibilidad con versiones anteriores. obj[prop] = value y obj.setAttributes(props, value) comportarse @ : [] haría que un detalle de implementación se filtrara a la superficie y contradeciera el enfoque centrado en javascript. Entonces, a menos que tengamos una especificación que haga lo siguiente, creo que es una mala idea: const data = <strong i="8">@myFunction</strong> // -> "[object Object]"

Si tengo que confiar en un componente web, me alegraría si la semántica se ocultara de React y JSX, además de asegurarme de que no introduzcan cambios importantes. De todas las opciones, dejar ref => ... en su lugar me parece favorable. ref está diseñado específicamente para acceder al objeto. Y al menos el desarrollador sabe exactamente lo que está sucediendo, no hay fugas de sigilo, ni nuevos atributos que puedan romper los proyectos existentes.

@LeeCheneler

La opción 3 sería mi preferida, pero eso es un gran cambio radical ... ¿Qué pasa con la inversa? ¿Los atributos tienen un prefijo, no accesorios?

¿Por qué sería un cambio radical? Se mantendría el comportamiento actual de los atributos que son los predeterminados. El sigilo sería opcional y los desarrolladores lo usarían para reemplazar los puntos en su código donde actualmente usan ref para pasar datos a un elemento personalizado como una propiedad JS.

@drcmda

ni nuevos atributos que puedan romper proyectos existentes.

¿Puede aclarar lo que quiso decir con esto?

Para su información, para cualquiera que siga la discusión, he actualizado el RFC con una quinta opción sugerida por los miembros del equipo de React.

@robdodson

Me estaba refiriendo a esto:

Opción 4: agregar un objeto de atributos
Contras
Puede ser un cambio rotundo

La opción 5 parece la más segura para nosotros. Nos permite agregar la función sin tener que tomar una decisión sobre la API "implícita" en este momento, ya que el ecosistema aún se encuentra en la fase de "averiguarlo". Siempre podemos volver a visitarlo en unos años.

El elemento de entrada de papel de Polymer tiene 37 propiedades, por lo que produciría una configuración muy grande. Si los desarrolladores usan muchos elementos personalizados en su aplicación, eso puede equivaler a muchas configuraciones que necesitan escribir.

Mi impresión es que los usuarios de elementos personalizados en React eventualmente querrán envolver algunos elementos personalizados en componentes de React de todos modos para el comportamiento / personalizaciones específicos de la aplicación. Es una mejor estrategia de migración para este caso si todo ya es un componente de React, por ejemplo

import XButton from './XButton';

y eso pasa a ser generado por

export default ReactDOM.createCustomElementType(...)

Esto les permite reemplazar un componente de React con un componente personalizado que usa (o incluso no usa) elementos personalizados en cualquier momento.

Por lo tanto, si las personas van a crear componentes de React en puntos de interoperabilidad, también podríamos proporcionar un ayudante poderoso para hacerlo. También es probable que las personas compartan esas configuraciones para los elementos personalizados que utilizan.

Y eventualmente, si vemos que el ecosistema se estabiliza, podemos adoptar un enfoque sin configuración.

Creo que el siguiente paso aquí sería escribir una propuesta detallada sobre cómo debería verse la configuración para satisfacer todos los casos de uso comunes. Debería ser lo suficientemente convincente para los usuarios de elementos personalizados + React, ya que si no responde a casos de uso comunes (como el manejo de eventos), terminaremos en el limbo donde la función no proporciona suficientes beneficios para compensar la verbosidad. .

Partiendo de mi comentario anterior , ¿qué tal:

const XFoo = ReactDOM.createCustomElementType('x-foo', {
  propName1: {
    propertyName: string | null,
    attributeName: string | null,
    attributeSerializer: function | null,
    eventName: string | null,
  }
  propName2: {
  }
  ...
});

La lógica sería entonces, para cada accesorio de React en una instancia de XFoo:

  1. Si eventName para ese prop no es nulo, regístrelo como un controlador de eventos que invoca el valor de prop (se supone que es una función).
  2. De lo contrario, si la representación del lado del cliente y propertyName no es nula, establezca la propiedad del elemento en el valor de prop.
  3. De lo contrario, si attributeName no es nulo, establezca el atributo del elemento en el valor de prop en cadena. Si attributeSerializer no es nulo, utilícelo para especificar el valor de la propiedad. De lo contrario, simplemente haga '' + propValue .

El elemento de entrada de papel de Polymer tiene 37 propiedades, por lo que produciría una configuración muy grande.

Me gustaría sugerir que la configuración solo sea necesaria para accesorios atípicos. Para cualquier accesorio en la instancia de XFoo que no esté incluido en la configuración, por defecto es:

  • si el valor es una función:
eventName: the prop name,
  • demás:
propertyName: the prop name,
attributeName: camelCaseToDashCase(the prop name),

Alternativamente, tal vez tenga sentido mantener los eventos en un espacio de nombres separado, en cuyo caso, elimine todo lo que tenga que ver con eventName del último comentario y, en su lugar, deje que los eventos se registren como:

<XFoo prop1={propValue1} prop2={propValue2} events={event1: functionFoo, event2: functionBar}>
</XFoo>

@gaearon @effulgentsia ¿qué piensan de una combinación de la opción 1 y la opción 5?

La opción 1 facilitaría al usuario ocasional de un elemento personalizado pasar datos enriquecidos. Me estoy imaginando el escenario en el que estoy creando una aplicación y solo quiero usar un par de elementos personalizados. Ya sé cómo funcionan y no estoy tan involucrado como para querer escribir una configuración para ellos.

La opción 5 sería para las personas que desean usar algo como la entrada en papel en toda su aplicación y realmente les gustaría exponer su API completa a todos los miembros de su equipo.

Para SSR de la opción 1, la heurística siempre podría usar un atributo si se representa en el servidor. Una propiedad camelCase se convierte en un atributo dash-case. Ese parece ser un patrón bastante común en las bibliotecas de componentes web.

Me gusta mucho la idea de una combinación option1 + option5. Lo que significa que para la mayoría de los elementos personalizados:

<x-foo prop1={propValue1}>

funcionaría como se esperaba: prop1 establecido como una propiedad del lado del cliente y como un atributo (en mayúsculas y minúsculas) del lado del servidor.

Y la gente podría cambiar a la opción 5 para cualquier cosa para la que lo anterior no les convenga.

Sin embargo, sería un cambio radical con respecto a la forma en que funciona React 16. Para cualquiera que experimente esa ruptura (por ejemplo, estaba usando un elemento personalizado con atributos que no están respaldados por propiedades), podría cambiar a la opción 5, pero aún así es una ruptura. Dejo que el equipo de React decida si eso es aceptable.

Ah, esto es lo que obtengo por leer esto rápidamente en el tren @robdodson 🤦‍♂️ ... No soy realmente un fanático de la opción 3 ahora 🤔 Lo leí como un todo en los accesorios con el prefijo, de ahí mi vacilación.

La opción 5 parece razonable y sencilla.

Me gusta hacia dónde se dirige @effulgentsia . ¿Hay alguna razón por la que no podría ser?

const XFoo = ReactDOM.createCustomElementType('x-foo', {
  propName1: T.Attribute,
  propName2: T.Event,
  propName3: T.Prop
})

¿O es valioso admitir varios tipos en un solo accesorio?

Dudaría con este flujo aunque

si el valor es una función:
eventName: el nombre del accesorio,
demás:
propertyName: el nombre de la propiedad,
atributoName: camelCaseToDashCase (el nombre del accesorio),

No creo que me gustaría que una función de apoyo se estableciera de forma predeterminada en un evento, y ¿es sensato asignar tanto propertyName como attributeName? ¿Cuándo desearía que ambos fueran compatibles para imitar la pregunta anterior? 🙂

@LeeCheneler :

Citando las ventajas de la Opción 1 del resumen de problemas:

Cualquier elemento creado con bibliotecas como Polymer o Skate generará automáticamente propiedades para respaldar sus atributos expuestos. Todos estos elementos deberían "simplemente funcionar" con el enfoque anterior. Se recomienda a los desarrolladores que creen componentes vanilla a mano que respalden atributos con propiedades, ya que reflejan cuán modernos (es decir, no extraños como <input> ) elementos HTML5 ( <video> , <audio> , etc.) se han aplicado.

Entonces, esa es la razón por la que asignar tanto propertyName como attributeName es sensato: porque refleja lo que realmente es el caso de los elementos que siguen las mejores prácticas. Y al hacer que React sea consciente de eso, le permite a React decidir cuál usar en función de la situación: como usar propiedades para la representación del lado del cliente y atributos para la representación del lado del servidor. Para los elementos personalizados que no siguen las mejores prácticas y tienen algunos atributos sin las propiedades correspondientes y / o algunas propiedades sin los atributos correspondientes, React debería ser consciente de eso, para que las propiedades sin atributos no se representen durante SSR y propiedad. -less-attribute se puede establecer con setAttribute () durante la renderización del lado del cliente.

Con su propuesta, eso podría potencialmente hacerse mediante indicadores de combinación de bits, como:

propName1: T.Property | T.Attribute,

Sin embargo, eso no proporcionaría una forma de expresar que el nombre del atributo es diferente del nombre de la propiedad (por ejemplo, camelCase a dash-case). Tampoco proporcionaría una forma de expresar cómo serializar un objeto rico en un atributo durante SSR (el comportamiento actual de "[objeto Objeto]" no es útil).

No creo que me gustaría que una función de apoyo se estableciera de forma predeterminada en un evento

Sí, creo que también estoy de acuerdo con eso, de ahí el comentario de seguimiento . ¡Gracias por validar mi vacilación con eso!

Aquí hay un pensamiento para una versión menos detallada de mi sugerencia anterior :

const XFoo = ReactDOM.createCustomElementType('x-foo', {
  UNREFLECTED_ATTRIBUTES: [
    'my-attr-1',
    'my-attr-2',
  ],
  UNREFLECTED_PROPERTIES: [
    'myProp1',
    'myProp2',
  ],
  REFLECTED_PROPERTIES: {
    // This is default casing conversion, so could be omitted.
    someVeryLongName1: 'some-very-long-name-1',

    // In case anyone is still using all lowercase without dashes.
    someVeryLongName2: 'someverylongname2',

    // When needing to define a function for serializing a property to an attribute.
    someRichData: ['some-rich-data', JSON.stringify],
  },
});

Y de acuerdo con el comentario del código anterior, recomiendo encarecidamente que no se requiera que se definan todas las propiedades reflejadas, sino que, por defecto, cualquier cosa que no esté definida como una propiedad reflejada automáticamente cuyo nombre de atributo sea la versión en mayúsculas y minúsculas.

Tiene sentido @effulgentsia 👍

Me gusta su segundo ejemplo, pero ¿no está abierto a una explosión combinatoria si se agregan más tipos, eventos de ala + lo que tenga sentido?

- UNREFLECTED_ATTRIBUTES
- UNREFLECTED_PROPERTIES
- UNREFLECTED_EVENTS
- REFLECTED_PROPERTIES_ATTRIBUTES
- REFLECTED_PROPERTIES_EVENTS
- REFLECTED_ATTRIBUTES_EVENTS
- REFLECTED_PROPERTIES_ATTRIBUTES_EVENTS
...

Aunque supongo que no querrás mezclar un evento con un accesorio o atributo de todos modos, el atributo y el accesorio son probablemente las únicas cosas que querrías imitar.

Creo que hay una oportunidad aquí para que tanto la comunidad de React como de Web Component se alineen con una mejor práctica. Reaccionar teniendo una opinión aquí contribuirá en gran medida a que los autores de elementos personalizados sean guiados en la dirección correcta debido a su adopción generalizada y al peso que tienen sus opiniones.

Aunque soy el autor de la implementación de la opción 4, siempre estoy atrapado por tener que separar los atributos y eventos de las propiedades. Idealmente, preferiría la opción 1. Prácticamente, creo que preferiría la opción 2 con una trampilla de escape.

La opción 1 es ideal, pero hay muchos tipos de atributos que no tienen propiedades correspondientes (aria / data), por lo que requeriría heurísticas adicionales alrededor de estos y si hay casos extremos en los que los elementos pueden tener un atributo que debería tener un propiedad, pero no implemente uno por cualquier motivo. Siento que esta es una opción que debe considerarse cuidadosamente, pero puede ser viable a largo plazo.

La opción 2 es preferible porque es un cambio inquebrantable. Funcionará para todas las situaciones en las que la definición del elemento personalizado se registre antes de que se cree una instancia (o se cargue antes de que se establezcan las propiedades). La técnica anterior para esta opción es Preact (cc @developit) y ha funcionado bien hasta ahora. Seguir este camino proporciona una implementación razonablemente sólida y sin interrupciones que ha sido probada por una variante de React exitosa y funciona en la mayoría de las situaciones. En todo caso, le da a React una solución a corto plazo mientras se evalúan mejores soluciones a largo plazo.

Para la situación (¿situación s ?) En la que no funciona - carga diferida de definiciones de elementos personalizados y cualquier otro que no hayamos cubierto - se podría implementar una trampilla de escape como la que ha hecho Incremental DOM . Esto es similar a lo que propone @effulgentsia , pero se x número de elementos personalizados. Si los consumidores quieren hacerlo por elemento personalizado, aún pueden hacerlo, porque es solo una función. Esto permite a React tener una opinión, escapar de la trampilla y satisfacer todos los casos de uso al traspasar la responsabilidad al consumidor por todos los casos extremos. Esto también es algo que hablé previamente con

Acerca de la preocupación por los atributos HTML futuros, esto es algo que no podemos resolver, por lo que no creo que debamos quedarnos atrapados en el tema aquí.

Esas llamadas ReactDOM.defineCustomElementProp() podrían proporcionarse en un archivo JS mantenido por el autor del elemento personalizado

Esto vincularía la implementación del elemento personalizado a la implementación específica de la biblioteca (en este caso React). En mi opinión, eso es una carga demasiado alta para los autores de elementos personalizados. Además, para los autores que no usan React, convencerlos de que envíen la definición entra en discusiones políticas que nadie quiere.

Definir la invocación de dicha API por parte del usuario de un elemento personalizado con solo las propiedades / atributos que el usuario realmente usa es menos código que mantener y más expresivo.

Incluso si el autor de una biblioteca no usa React, estoy argumentando que es una mejor UX para el cliente de esos componentes web definir la configuración de la propiedad una vez y luego usarla.

La primera vez que usa un componente web que necesita una propiedad, es tan difícil como agregar un sigilo (más detallado, pero también más explícito), pero luego es más fácil para usos posteriores porque no necesita pensar en ello para cada sitio de llamadas de ese componente. En cualquier caso, debe comprender la diferencia de propiedades / atributos al adoptar un nuevo componente web, pero con la configuración compartida única no necesita que todos los usuarios de ese componente dentro de la misma aplicación piensen en ello.

Potencialmente, podríamos permitir la reasignación de nombres de propiedades en esa configuración para permitir crear más nombres idiomáticos de React allí. La ruta de actualización a un contenedor más sofisticado, si alguna vez fuera necesario, también es más suave, ya que puede reemplazar XFoo con un componente React personalizado que hace cualquier lógica de traducción sofisticada que necesite.

Básicamente: si es posible que las personas no piensen en la configuración de la propiedad cada vez que usan un componente, prefiero ese enfoque. Por eso sugerí la opción 5. Creo que es más ergonómica que la 3 y la 4 y, al mismo tiempo, es igualmente flexible.

Las opciones 1 y 2 no funcionan muy bien con la representación del servidor (generación HTML), y la opción 2 también crea riesgos de actualización para los autores de componentes web, donde ahora agregar una propiedad con el mismo nombre que un atributo existente es un cambio radical. Si decidimos que SSR no es útil, la opción 1 es bastante atractiva desde la perspectiva de React. Sin embargo, no estoy familiarizado si sería lo suficientemente completo en la práctica: ¿los autores de componentes exponen todo a través de propiedades?

@treshugart sin desprendimiento de bicicletas , ¿cree que podría mostrar cómo espera que funcione la "escotilla de escape"? El pseudocódigo está bien. Así que todos estamos en la misma página.

Lo siento @sophiebits, acabo de ver tu respuesta. Me gustaría responder a algunos de los puntos después de leerlo varias veces más, pero quería responder rápidamente a este:

Sin embargo, no estoy familiarizado si sería lo suficientemente completo en la práctica: ¿los autores de componentes exponen todo a través de propiedades?

Cualquier componente construido con Polymer o Skate expondrá todo a través de propiedades. Puede haber algunos valores atípicos raros (tal vez establecer un atributo solo para el estilo o algo así) pero, de lo contrario, creo que las cosas siempre están respaldadas por propiedades. Estoy bastante seguro de que la mayoría de los componentes web en producción se crean utilizando una de estas dos bibliotecas.

Si alguien está escribiendo manualmente un componente web básico, no hay nada que lo obligue a exponer todo a través de las propiedades, pero lo alentamos a que lo haga en nuestros documentos y ejemplos de mejores prácticas .

Por lo que vale, tampoco hay nada que los obligue a usar atributos. Una definición de elemento personalizado es solo una clase para que el desarrollador pueda hacer lo que quiera. Pero en la práctica, la mayoría de la gente prefiere usar una abstracción como Polymer o Skate para acuñar sus componentes, por lo que obtienen una API de propiedades de forma gratuita.

Quizás, entonces, el mejor enfoque es hacer propiedades siempre en el lado del cliente y luego admitir un mapa de configuración al estilo de la Opción 5 de nombres de propiedades primitivos para los nombres de atributos para las personas que desean admitir SSR en sus aplicaciones.

@sophiebits : Creo que es una gran idea, excepto en la medida en que es un cambio radical para las personas que han escrito sus aplicaciones React usando nombres de atributos. Por ejemplo:

<x-foo long-name={val} />

Pero, ¿qué pasa con una regla para "hacer propiedades" si el nombre de propiedad no tiene guiones y "hacer atributos" si los tiene?

@robdodson : ¿conoce algún marco o práctica de elementos personalizados en el que las personas tengan diferentes tipos de atributos de la propiedad correspondiente, sin contener un guión? Es decir, un atributo longname con una propiedad longName ? En realidad, ese parece ser el patrón mucho más común con elementos HTML incorporados y atributos globales (por ejemplo, contenteditable => contentEditable ), pero no tengo claro si ahora está lo suficientemente desaconsejado para atributos de elementos personalizados gracias a Polymer y Skate. Si sigue siendo un problema, entonces JSX existente con:

<x-foo longname={val} />

fallaría como conjunto de propiedades si la propiedad es longName .

@effulgentsia Solo puedo hablar por Skate, pero solo recomendamos usar el atributo API si escribes HTML, que, para todos los efectos, también se puede clasificar como renderizado de servidor. Si está haciendo algo a través de JS, debería establecer accesorios. Nuestra props API realiza automáticamente una sincronización / deserialización unidireccional de los atributos, y deriva el nombre del atributo colocando en mayúsculas el nombre de la propiedad (camelCase se convierte en camel-case, etc.). Este comportamiento en su conjunto es configurable pero recomendamos que la mejor práctica sea la mencionada anteriormente.

Como muchos afirman, los accesorios hacen que SSR sea difícil, y esta es una preocupación válida. Dado que parece que la mayoría preferiría la opción 1, ¿tal vez intentemos avanzar con la propuesta de @sophiebits de establecer accesorios en el cliente mientras proporcionamos un respaldo / mapeo? Supongo que esto significa que los atributos se establecerán en el servidor.

Por el bien de un ejemplo, así es como podría implementar la transferencia de estado y accesorios del servidor al cliente con Skate (y cualquier elemento personalizado que implemente un renderedCallback() y props y / o state setters. Hacerlo a nivel de elemento personalizado es trivial. Si React siguiera el camino de establecer atributos en el servidor, la rehidratación sería esencialmente la misma. Los autores de componentes de skate en realidad no tendrían que hacer nada como nosotros ya les proporcionaría la lógica de deserialización.

@robdodson Haría algo similar a lo que está haciendo Incremental DOM. Esto se vería algo así como:

const isBrowser = true; // this would actually do the detection
const oldAttributeHook = ReactDOM.setAttribute;

// This is much like the IDOM impl but with an arguably more clear name.
ReactDOM.setAttribute = (element, name, value) => {
  // This is essentially option 2, but with the added browser check
  // to keep attr sets on the server.
  if (isBrowser && name in element) {
    element[name] = value;
  } else {
    oldAttributeHook(element, name, value);
  }
};

@sophiebits

La opción 2 también crea riesgos de actualización para los autores de componentes web, donde ahora agregar una propiedad con el mismo nombre que un atributo existente es un cambio radical.

Creo que esto puede ser inevitable dada la forma en que funcionan los atributos en la plataforma. Si SSR un elemento personalizado y, por lo tanto, debe escribir en un atributo, también corre el riesgo de que la plataforma envíe un atributo con un nombre similar en el futuro. Como @treshugart mencionó antes , no creo que sea algo que React (o realmente cualquier biblioteca) tenga la capacidad de resolver. Esto es algo que quiero discutir con los autores de especificaciones de componentes web en TPAC para ver si podemos solucionarlo en el espacio de elementos personalizados.

No estoy seguro de si eso le cambia de opinión acerca de la opción 2 😁, pero quería mencionarlo ya que la opción 2 tiene algunas bonificaciones agradables y Preact parece estar demostrando su eficacia en la práctica.

Quizás, entonces, el mejor enfoque es hacer propiedades siempre en el lado del cliente y luego admitir un mapa de configuración al estilo de la Opción 5 de nombres de propiedades primitivos para los nombres de atributos para las personas que desean admitir SSR en sus aplicaciones.

+1, apoyo la idea de seguir avanzando en esta dirección.


@effulgentsia

Creo que es una gran idea, excepto en la medida en que es un cambio radical para las personas que han escrito sus aplicaciones React usando nombres de atributos.

La opción 2 resolvería eso :)
De lo contrario, probablemente se necesitaría una heurística junto con la opción 1 que mapee atributos en mayúsculas y minúsculas a propiedades camelCased.

Pero, ¿qué pasa con una regla para "hacer propiedades" si el nombre de propiedad no tiene guiones y "hacer atributos" si los tiene?

Parece que Preact establecerá el atributo si hay un guión en el nombre. Supongo que eso se debe a que usan la opción 2 y long-name no pasa la verificación in , por lo que recurre a un atributo ( fuente ).

Personalmente, me gusta este comportamiento, pero vuelve al ámbito de establecer atributos y posibles conflictos futuros, así que creo que @sophiebits debería

¿Conoce algún marco o práctica de elementos personalizados en el que las personas tengan diferentes atributos de mayúsculas y minúsculas de la propiedad correspondiente, sin contener un guión?

No que yo sepa. El tablero es una manera fácil para que la biblioteca sepa dónde colocar el estuche de camellos. Si estuviera creando manualmente un componente web vanilla, podría hacer longname/longName y solo la opción 2 lo salvaría porque no encontraría una propiedad longname , pero volvería a la utilizada anteriormente longname atributo.

Por lo que vale, parece que Preact, como último recurso, llamará toLowerCase() en una propiedad que no reconoce antes de establecer el atributo. Entonces, si estuviera SSR'ing <x-foo longName={bar}/> , también volvería correctamente al atributo longname="" .


@treshugart

¿Quizás intentamos seguir adelante con la propuesta de @sophiebits de establecer accesorios en el cliente mientras proporcionamos un respaldo / mapeo? Supongo que esto significa que los atributos se establecerán en el servidor.

si y si.

Haría algo similar a lo que está haciendo Incremental DOM

¿Es la idea de que cada elemento (o su clase base) necesitaría mezclar esto?

Por cierto, gracias a todos por seguir participando en la discusión, sé que se ha vuelto bastante larga ❤️

No estoy seguro de entender el último ejemplo en https://github.com/facebook/react/issues/11347#issuecomment -339858314 pero es muy poco probable que proporcionemos un gancho anulable global como este.

@gaearon Creo que Trey solo estaba mostrando el cambio neto como un parche de mono, presumiblemente estaría escrito en la implementación de ReactDOM.

En base a los comentarios recientes, ¿qué opinan todos de esta propuesta?

  1. Para BC, no cambie nada sobre cómo funcionan los elementos personalizados en minúsculas en React. En otras palabras, JSX como <x-foo ... /> continuaría funcionando de la misma manera en React 17 que en 16. O, si se necesitan correcciones de errores _ menores_, abra un tema separado para discutirlos.

  2. Agregue una API sin configuración para crear un componente React con una mejor semántica de elementos personalizados. Por ejemplo, const XFoo = ReactDOM.customElement('x-foo'); .

  3. Para el componente creado anteriormente, utilice la siguiente semántica:

  4. Para cualquier accesorio en XFoo que sea un nombre de accesorio de React reservado ( children , ref , ¿cualquier otro?), Aplique la semántica de React habitual (no lo establezca como un atributo o propiedad en el nodo DOM del elemento personalizado).
  5. Para cualquier atributo o propiedad global de HTMLElement definido por la especificación HTML (incluidos data-* y aria-* ), haga lo mismo con esos accesorios que lo que React haría con ellos si estuvieran en un div elemento. En otras palabras, cómo React aplica los accesorios en <XFoo data-x={} className={} contentEditable={} /> al nodo DOM debería ser idéntico a cómo lo hace para <div data-x={} className={} contentEditable={} /> (tanto para el lado del cliente como para SSR).
  6. Para cualquier accesorio en XFoo que contenga un guión (que no sean los atributos globales permitidos anteriormente), emita una advertencia (para informar al desarrollador que XFoo es una API centrada en la propiedad, no en los atributos) y no haga nada con ella (ni configúrelo como una propiedad ni un atributo).
  7. Para cualquier accesorio en XFoo que comience con un guión bajo o una letra ASCII superior, no haga nada con él (¿y tal vez emita una advertencia?). Tampoco es una forma recomendada de nombrar propiedades para elementos personalizados, así que reserve esos espacios de nombres para la semántica futura (por ejemplo, vea el n. ° 4 a continuación).
  8. Para todos los demás accesorios en XFoo, al renderizar el lado del cliente, configúrelo en el nodo DOM como una propiedad. Para SSR, si el valor es primitivo, renderícelo como un atributo; si no es primitivo, omita el renderizado y configúrelo como una propiedad del lado del cliente durante la hidratación. Para la representación SSR como atributo, camelCaseToDashCase el nombre.

  9. Para los componentes creados a través de la API en el n. ° 2 anterior, reserve un nombre de propiedad para usar con los oyentes de eventos. Por ejemplo, 'events' o 'eventListeners' . O, si no desea entrar en conflicto con esos posibles nombres de propiedad de elementos personalizados, entonces '_eventListeners' o 'EventListeners' . La implementación creada por ReactDom de XFoo registraría automáticamente estos oyentes de eventos en el nodo DOM.

  10. Para casos extremos (por ejemplo, usar un elemento personalizado para el cual la implementación anterior no es deseada o suficiente), el desarrollador de la aplicación podría implementar su propio componente React para hacer cualquier cosa especial que necesite. Es decir, no necesitan usar ReactDOM.customElement() o podrían extenderlo.

  11. Para las personas que desean todo el comportamiento anterior, pero desean que su JSX use los nombres de elementos personalizados en minúsculas ( <x-foo /> lugar de <XFoo /> , por razones similares que las personas familiarizadas con la escritura HTML prefieren <div> sobre <Div> ), pueden parchear React.createElement (). Sería un parche de mono bastante simple: simplemente tome el primer argumento y si coincide con el nombre de un elemento personalizado para el que desea esto (ya sea una lista específica o cualquier cadena con todas las letras minúsculas y al menos un guión), luego invoque ReactDOM.customElement() en ese nombre y reenvíe el resultado y los argumentos restantes al React.createElement() .

@developit @gaearon podría ser cualquiera. Si fuera necesario un "mapeo", creo que un anzuelo es más sostenible. Sin embargo, eso también tenía la intención de mostrar el cambio neto si se implementara en el núcleo de ReactDOM, como señaló Jason.

Para BC, no cambie nada sobre cómo funcionan los elementos personalizados en minúsculas en React. En otras palabras, JSX como continuaría funcionando de la misma manera en React 17 que en 16. O, si se necesitan correcciones de errores menores, abra un tema separado para discutirlos.

Personalmente, preferiría poder usar mi elemento <x-foo> , tal como está, y pasarle propiedades fácilmente sin necesidad de envolverlo primero.

Si es posible, prefiero ir con la sugerencia de @sohpiebits de la opción 1 (propiedades del lado del cliente) y 5 (configuración para SSR). Todavía tengo la esperanza de que tal vez la gente reconsidere la opción 2 (¿tal vez la opción 2 + 5?) Para obtener el bono de compatibilidad hacia atrás.

@gaearon, ¿

Mi principal preocupación con la Opción 5 es crear una gran cantidad de texto estándar para permitir la interoperabilidad, rompiendo el DX, sería una pena para todo el equipo central de React y los colaboradores dedicar mucho tiempo a cambios que no tendrán el impacto deseado.

Realmente me encantó cómo el equipo de React manejó los últimos cambios en el repositorio, como _PropTypes_, sería bueno pensar en un plan que involucre más de un solo cambio, para educar a los desarrolladores sobre los posibles cambios que pueden hacer en el futuro para personalizar y no personalizar elementos.

Creo que la solución que nos satisfará a todos será una que combine algunas de estas opciones como _pasos_, adición de API, advertencia y desaprobación o cambio de comportamiento.

Quizás las opciones 5, con advertencia, luego la opción 2 con el desuso del contenedor necesario.

Quizás las opciones 5, con advertencia, luego la opción 2 con el desuso del contenedor necesario.

De hecho, estaba pensando que hacer lo contrario sería una mejor serie de pasos. Opción 1 o 2 porque es un cambio menos dramático. Y midiendo cómo va eso y cómo comienza a verse la historia de SSR para los componentes web. Luego, continúe con la opción 5 porque agrega una nueva API y una noción de componentes de proxy / envoltorio.

El principal problema con la opción 1 es que se trata de una ruptura de BC bastante grande. El principal problema con la opción 2 es que tiene una condición de carrera que depende de cuándo se termina la actualización del elemento. No estoy seguro de que ninguno de ellos sea realmente aceptable para un proyecto que se usa tan ampliamente como React.

Dada la disponibilidad de la opción 5, me pregunto si podemos hacer mejoras mucho más seguras, pero útiles, para los usos que no son de la opción 5. Por ejemplo, ¿qué tal lo inverso de la opción 4: simplemente introduzca domProperties y eventListeners props, para que pueda hacer cosas como:

<x-foo 
  my-attr1={...} 
  domProperties={{myRichDataProperty: ...}} 
  eventListeners={{'a-custom-element-event':  e => console.log('yo')}} 
/>

Esto es totalmente compatible con versiones anteriores, porque en virtud de sus letras mayúsculas, domProperties y eventListeners no son nombres de atributo válidos . Excepto por eso, React 16 actualmente llama a setAttribute () incluso para nombres de accesorios con letras mayúsculas, confiando en los navegadores para poner el nombre en minúsculas internamente; sin embargo, ¿podría una futura versión menor de React 16 emitir una advertencia cuando encuentre accesorios de elementos personalizados que no sean nombres de atributos válidos, para que las personas puedan corregir sus errores de carcasa antes de actualizar a React 17?

Además de preservar BC, este enfoque es fácil de entender: está centrado en atributos (lo que significa que los accesorios de React se tratan como atributos de elemento), lo que encaja dado que el nombre del elemento en sí es todo en minúsculas y tiene un guión y, por lo tanto, está centrado en HTML. Sin embargo, se proporciona domProperties para los casos relativamente raros en los que necesita pasar propiedades, como para pasar datos enriquecidos.

Para las personas que desean cambiar su modelo mental para centrarse en la propiedad (ala Opción 1), ahí es donde podría entrar una API de estilo de la opción 5:

const XFoo = ReactDOM.customElement('x-foo');
<XFoo prop1={} prop2={} data-foo={} aria-label={} />

Con esta sintaxis, cada prop se trata como una propiedad (opción 1). Lo cual encaja, porque el elemento "nombre" (XFoo) también sigue una convención centrada en JS. Creo que todavía querríamos, como mínimo, admitir data-* y aria-* props tratados como atributos, que podríamos limitar solo a esos, o generalizar para tratar cualquier prop con un guión como un atributo.

Mientras tanto, para admitir la configuración de la opción 5 de SSR, podríamos agregar una API de configuración a ReactDOM.customElement , como:

const XFoo = ReactDOM.customElement('x-foo', ssrConfiguration);

¿Quizás ssrConfiguration podría ser una función de devolución de llamada similar a la ReactDOM.setAttribute comentario de @treshugart ?

¿Qué piensas?

@effulgentsia Me gusta adónde van tus pensamientos. Cambiando un poco los nombres en bicicleta: domProps / domEvents . Esto está muy cerca de la opción 4.

WRT SSR, creo que SSR puede ser manejado por los propios elementos personalizados siempre que React pueda respetar los atributos, el componente muta en sí mismo si no los está rastreando. Publiqué una esencia antes, pero aquí está nuevamente, por conveniencia: https://gist.github.com/treshugart/6eff0da3c0bea886bb56589f743b78a6. Básicamente, el componente aplica atributos después de la renderización en el servidor y los rehidrata en el cliente. SSR para componentes web es posible, pero las soluciones aún se están discutiendo a nivel de estándares, por lo que puede ser mejor esperar a esta parte de la propuesta.

@effulgentsia También me gusta hacia dónde te @sophiebits , @gaearon do
¿Tienen pensamientos sobre esta dirección?

El martes 31 de octubre de 2017 a las 7:33 p.m., Trey Shugart [email protected] escribió:

@effulgentsia https://github.com/effulgentsia Me gusta donde tu
pensamientos van. Cambiando un poco los nombres en bicicleta, podría ser útil
alinear sus nombres: domProps / domEvents, o algo así.

Opción 2 de WRT, es al menos compatible con versiones anteriores y resuelve la mayoría de los casos de uso,
pero estoy llegando a las alternativas.

Creo que SSR puede ser manejado por los propios elementos personalizados siempre que
React podría respetar los atributos en los que el componente muta en sí mismo si es
sin rastrearlos. Publiqué una esencia antes, pero aquí está de nuevo, para
conveniencia:
https://gist.github.com/treshugart/6eff0da3c0bea886bb56589f743b78a6.
Básicamente, el componente aplica atributos después de renderizar en el servidor.
y los rehidrata en el cliente. SSR para componentes web es posible, pero
Las soluciones aún se están discutiendo a nivel de estándares, por lo que puede ser
mejor esperar en esta parte de la propuesta aquí.

-
Recibes esto porque te mencionaron.
Responda a este correo electrónico directamente, véalo en GitHub
https://github.com/facebook/react/issues/11347#issuecomment-340960798 ,
o silenciar el hilo
https://github.com/notifications/unsubscribe-auth/ABBFDeiQhBWNGXNplbVV1zluYxT-ntFvks5sx9hngaJpZM4QD3Zn
.

Bikeshedding los nombres un poco: domProps / domEvents.

Me gustan esos. También estoy haciendo una lluvia de ideas sobre si hay una manera de hacerlos aún más idiomáticos para React reemplazando el concepto de "dom" con el concepto de "ref". En otras palabras: refProps / refEvents , ya que se trata de adjuntar accesorios y oyentes de eventos a la "ref".

Y luego pensé, ¿qué pasa si en lugar de introducir nuevos nombres especiales dentro de this.props , simplemente sobrecargamos el atributo ref JSX existente? Actualmente, "ref" es una función que se llama cuando se monta y desmonta el componente React. ¿Qué pasa si permitimos que también sea un objeto como tal?

<x-foo my-attr-1={}
  ref={{
    props: ...
    events: ...
    mounted: ...
    unmounted: ...
  }}
/>

Solo una idea.

Realmente me gusta este enfoque :)

Ping amistoso en esto. @gaearon @sophiebits ¿Tienen alguna opinión sobre la última propuesta de @effulgentsia ? Solo tengo curiosidad por saber si está en el estadio de béisbol o si no es titular.

Acabamos de abrir un proceso de RFC. ¿Podríamos pedirles a alguno de ustedes que lo envíe como RFC?
https://github.com/reactjs/rfcs

Preferiría no agregar domProperties y eventListeners "props" (o el equivalente dentro de un objeto ref = {{}}) porque hace que el uso de elementos personalizados sea muy antinatural y diferente a todos los demás componentes de React. Si el usuario del componente va a necesitar conocer la diferencia entre propiedades y atributos, etc., entonces prefiero hacerlo como una solución de estilo ReactDOM.createCustomElementType. Entonces, si usa el componente exactamente una vez, es una cantidad de trabajo comparable (especificando la configuración y luego usándolo una vez), pero si usa el componente muchas veces, entonces no necesita pensar en el objeto de configuración cada vez. Requerir que la configuración se especifique cada vez parece frustrar los objetivos de tener una integración limpia de elementos personalizados a menos que me falte algo.

@sophiebits Está bien, podría intentar escribir un RFC para algo así. ¿Qué opinas de la idea que planteaste el 26 de

Al menos con el estilo createCustomElementType , las bibliotecas pueden asignar fácilmente sus API a eso. Es decir, nuestro skatejs / renderer-react podría tomar fácilmente el props y configurar el elemento personalizado para React. Sin embargo, esto deja a la gente de vainilla alta y seca sin una abstracción o sin realizar un poco de trabajo. Me gusta la sugerencia de Rob de un valor predeterminado seguro, al tiempo que permite un control detallado. ¿Es eso algo que funcionaría?

@robdodson Creo que estoy de acuerdo. No puedo prometer que no surjan otras preocupaciones, pero creo que se siente como un buen equilibrio.

La opción 3 es la mejor opción hasta ahora y puede funcionar con SSR, explicaré una idea de cómo.

Hasta ahora, todas las opciones por sí solas, excepto 3,

  • o bien crea una alta carga de mantenimiento para todos los que están fuera de la fuente de React
  • o obligan a todos los autores de elementos personalizados en todo el universo a cumplir con las reglas específicas de React.

Aquí están las reglas universales en las que todos estamos de acuerdo:

  • establecer atributos con setAttribute es la forma más estándar en el universo para pasar datos a elementos de una manera que coincida 1 a 1 con atributos HTML declarativos. Esto tiene que funcionar el 100% del tiempo como una ley del universo, por lo tanto, es la única forma 100% garantizada de pasar datos a los elementos.
  • algunas personas no están contentas de que haya sido diseñado solo para cuerdas.
  • Algunos elementos (repito, _sólo algunos elementos_ ), aceptan valores a través de propiedades de objeto que se asignan a ciertos atributos. Esto _no_ es algo en lo que se pueda confiar el 100% del tiempo.
  • a algunas personas les gustan las propiedades de los objetos porque pueden aceptar no cadenas

Entonces, como mínimo, si React quiere trabajar al 100% con _ cada elemento personalizado del universo y no ser una carga para las personas_ , entonces:

  • React necesita darse cuenta de que no es Dios, hay muchas otras bibliotecas que la gente usa además de reaccionar,
  • por lo tanto, React debería _por defecto_ simplemente pasar datos a través de setAttribute porque eso es 100% estándar.
  • React debería aceptar el hecho de que los autores de elementos personalizados pueden extender / anular los métodos setAttribute en sus definiciones de clase, haciendo que setAttribute acepte cosas que no sean cadenas. Un buen ejemplo pueden ser bibliotecas como A-frame.
  • React debería aceptar que si un autor de elemento personalizado quiere que un elemento personalizado funcione con todas las bibliotecas posibles, _no solo con React_, entonces ese autor se basará en setAttribute para hacer que su elemento sea compatible de forma predeterminada con todo, y si de forma predeterminada todas las bibliotecas se basan en atributos, entonces todo el universo funcionará entre sí. _¡No hay si, y o pero sobre esto! _ (A menos que el w3c / whatwg haga grandes cambios)

Soooooooo , dicho esto, deberíamos al menos

Luego, podemos pensar en las implicaciones de los elementos que a veces tienen una API de propiedad de objeto:

  • los desarrolladores tienen la opción de usar setAttribute sabiendo que funcionará todo el tiempo.
  • los desarrolladores a veces pueden aprovechar las propiedades de los objetos.
  • los desarrolladores siempre deben saber si existe o no una interfaz objeto-propiedad para cualquier elemento dado (personalizado o no).
  • Las interfaces de propiedad de objeto son una interfaz alternativa que no necesariamente se asigna 1 a 1 con atributos.

Entonces, con este conocimiento de atributos vs propiedades, una solución en React que _ desea aumentar el estándar del 100% y respetar las leyes del universo_ debería:

  1. Permitir que los atributos funcionen el 100% del tiempo de forma predeterminada. Esto significa que <x-foo blah="blah" /> debería _de forma predeterminada_ mapear a setAttribute y pasar el valor a lo largo de _unchanged_ . Este es un cambio inquebrantable. De hecho, es un cambio de corrección que de otra manera resultaría en la transferencia de una cadena "[object Object]" sin sentido.
  2. Piense en una forma alternativa de permitir que el usuario de React

Parece que la Opción 3, usar un sigilo (cuya sintaxis adicional, honestamente, no es difícil de aprender), es una solución que se acerca más a lo ideal. Basado en esta pregunta SO , entonces el único símbolo disponible es = , aunque decidirse por algo como & (con una forma escapable, tal vez como \& ) es más legible. Por ejemplo, si quiero un accesorio específicamente:

<x-foo &blah="blah" />

La mayoría de los demás caracteres cubiertos por la especificación de sintaxis HTML WHATWG deberían funcionar, y espero que lo hagan, pero ese es otro tema.

La opción 3 es la mejor opción hasta ahora.

Si la hidratación no se usa en el cliente y, por lo tanto, los accesorios no tienen sentido en SSR, entonces, bueno. Nunca funcionó antes y no necesita funcionar ahora. SSR de estilo PHP o Java envía HTML estático sin hidratación y se basan al 100% en los atributos. Dice que si usamos React SSR, probablemente usaremos la hidratación del lado del cliente, y si no queremos hidratación, entonces simplemente debemos ser conscientes del hecho de que no debemos usar accesorios en este caso. _Esto es simple. Todo lo que tiene que hacer reaccionar es dejar clara esta advertencia en la documentación.

¡¡¡Pero!!! ¡Eso no es todo! Aún podemos incluir las características de la Opción 5 para brindar a las personas más control. Con una API como la Opción 5,

  • Algunas personas pueden configurar cómo se pueden asignar algunos atributos a los accesorios.
  • Y cómo algunos accesorios pueden asignarse a atributos. ¡Esto puede ayudar a las personas a hacer que la SSR funcione incluso para el tipo de SSR no hidratante!
  • podemos dejar que la gente defina "si este atributo está escrito, pasarlo a este accesorio, pero si es SSR, no pasarlo al accesorio", o "pasar este accesorio a este atributo solo durante SSR; de lo contrario, hacer lo que Especifiqué el uso del sigilo ", etc.

Al final, lo siguiente parece una solución que funcionaría:

  • Opción 3 con setAttribute utilizado por defecto detrás de escena,
  • con una corrección para # 10070 para que React no se interponga en el camino de las personas,
  • y una API como en la Opción 5 para un mayor ajuste para aquellos que realmente lo necesitan, incluidas formas de ajustar SSR, cliente o ambos.

¡Feliz Año Nuevo!

Quiero responder a algunos de los puntos anteriores, pero temo que este hilo ya sea _increíblemente_ largo. Entonces, perdón por hacerlo más largo: P

Aquí están las reglas universales en las que todos estamos de acuerdo:

establecer atributos con setAttribute es la forma más estándar en el universo para pasar datos a elementos de una manera que coincida 1 a 1 con atributos HTML declarativos. Esto tiene que funcionar el 100% del tiempo como una ley del universo, por lo tanto, es la única forma 100% garantizada de pasar datos a los elementos.

Esto no es del todo cierto. No hay nada en la plataforma que obligue a que un elemento personalizado exponga una interfaz de atributos. Con la misma facilidad, podría crear uno que solo acepte propiedades JS. Por tanto, no es una "forma garantizada de pasar datos". La falta de mecanismos de aplicación significa que no puede confiar en ninguno de los estilos (atributos HTML o propiedades JS) con 100% de certeza.

algunas personas no están contentas de que haya sido diseñado solo para cuerdas.
Algunos elementos (repito, solo algunos elementos), aceptan valores a través de propiedades de objeto que se asignan a ciertos atributos. Esto no es algo en lo que se pueda confiar el 100% del tiempo.

Alentamos a las personas a que ni siquiera se molesten en crear atributos para propiedades que aceptan datos enriquecidos como objetos / matrices. Esto se debe a que algunos datos no se pueden volver a serializar en una cadena de atributos. Por ejemplo, si una de las propiedades de su objeto es una referencia a un nodo DOM, eso no se puede clasificar en cadena. Además, cuando se secuencia y analiza un objeto, pierde su identidad. Es decir, si tiene una referencia a otro POJO, en realidad no puede usar esa referencia ya que ha creado un objeto completamente nuevo.

a algunas personas les gustan las propiedades de los objetos porque pueden aceptar no cadenas

por lo tanto, React debe pasar los datos por defecto a través de setAttribute porque eso es 100% estándar.

Las propiedades de JavaScript son igualmente estándar. La mayoría de los elementos HTML exponen una interfaz de atributos y propiedades correspondientes. Por ejemplo, <img src=""> o HTMLImageElement.src .

React debería aceptar el hecho de que los autores de elementos personalizados pueden extender / anular los métodos setAttribute en sus definiciones de clase, haciendo que setAttribute acepte cosas que no sean cadenas.

Los autores podrían hacer eso, pero en realidad parece mucho más "no estándar" que simplemente usar propiedades JS. También puede exponerlo a problemas extraños relacionados con el análisis y la clonación del elemento .

React debería aceptar que si un autor de elemento personalizado quiere que un elemento personalizado funcione con todas las bibliotecas posibles, no solo con React, entonces ese autor se basará en setAttribute para hacer que su elemento sea compatible de forma predeterminada con todo, y si de forma predeterminada todas las bibliotecas se basan en atributos. , entonces todo el universo trabajará entre sí. ¡No hay si, y o pero sobre esto! (a menos que w3c / whatwg haga grandes cambios)

No estoy seguro de cómo llega a esta conclusión porque hay otras bibliotecas que prefieren configurar las propiedades de JS en elementos personalizados (Angular, por ejemplo). Para una máxima compatibilidad, los autores deben respaldar sus atributos con propiedades JS. Eso cubrirá la mayor superficie de usos posibles. Todos los elementos creados con Polymer hacen esto de forma predeterminada.

Permitir que los atributos funcionen el 100% del tiempo de forma predeterminada. Esto significa que debería, por defecto, mapear a setAttribute y pasar el valor sin cambios. Este es un cambio inquebrantable. De hecho, es un cambio de corrección que de otro modo resultaría en la transmisión de una cadena "[objeto Objeto]" sin sentido.

Creo que React en realidad _es_ pasando el valor sin cambios. Llamar a setAttribute('foo', {some: object}) da como resultado [object Object] . ¿A menos que esté proponiendo que llamen JSON.stringify() en el objeto? Pero ese objeto no es "sin cambios". Creo que tal vez estás confiando en que el autor ha anulado setAttribute() ? Puede ser más plausible animarlos a crear las propiedades JS correspondientes en lugar de parchear el DOM.

Creo que React en realidad _es_ pasando el valor sin cambios. Llamar a setAttribute('foo', {some: object}) da como resultado [object Object]

React está coaccionando valores a una cadena antes de pasar a setAttribute :

https://github.com/facebook/react/blob/4d6540893809cbecb5d7490a77ec7ad32e2aeeb3/packages/react-dom/src/client/DOMPropertyOperations.js#L136

y

https://github.com/facebook/react/blob/4d6540893809cbecb5d7490a77ec7ad32e2aeeb3/packages/react-dom/src/client/DOMPropertyOperations.js#L166

Básicamente estoy de acuerdo con todo lo que dijiste.

Estamos de acuerdo en que la gente hace las cosas en ambos sentidos y que no existe un estándar que obligue a todos a hacerlo de una forma u otra, así que sigo pensando

  • Opción 3 con setAttribute usado por defecto y un sigilo para especificar el uso de propiedades de instancia,
  • con una solución para # 10070 para que React no fuerce args a cadenas,
  • y la Opción 5, una API para un ajuste fino

Si la Opción 5 se hace bien, entonces la hidratación para las soluciones SSR puede asignar datos a atributos o accesorios, como se puede especificar mediante el uso de la API de la Opción 5.

React está coaccionando valores a una cadena antes de pasar a setAttribute

Veo. Dado que la mayoría de la gente no define un toString() , el [object Object] .

Dado que la mayoría de la gente no define un toString() , el [object Object] .

Como si lo hiciera

const div = document.createElement('div')
div.setAttribute('foo', {a:1, b:2, c:3})

el resultado es

<div foo="[object Object]"></div>

Obviamente, como desarrolladores web, debemos ser conscientes de lo que sucede cuando pasamos un elemento sin cadenas a los atributos del elemento. Por ejemplo, soy consciente de que puedo pasar no cadenas a elementos A-Frame, y debería tener la libertad de hacerlo sin que una biblioteca se interponga en el camino.

React necesita darse cuenta de que no es Dios, hay muchas otras bibliotecas que la gente usa además de reaccionar.

Esto es innecesariamente sarcástico. Notarás en este hilo que nos preocupamos por esto, pero que hay muchas opciones y visiones diferentes sobre dónde llevar el diseño de elementos personalizados. Ciertamente no es obvio qué se debe hacer.

@sebmarkbage Lo siento, no quise ser sarcástico en absoluto, y creo que React es una buena biblioteca. Debería haber pensado en mis palabras con más cuidado allí (especialmente porque no todos tienen la misma religión).

Lo que quise decir es que React es muy popular, por lo que React tiene el potencial de influir en las personas para que hagan cosas de cierta manera que pueden no funcionar en otros lugares (por ejemplo, podría decirles a las personas que confíen en las propiedades de instancia para todos los elementos personalizados que no funcionarían). No funciona con todos los elementos personalizados).

React actualmente convierte todos los valores pasados ​​a los atributos del elemento en cadenas. Si React no hubiera hecho esto, por ejemplo, no habría necesidad de que exista un marco-react (que soluciona el problema de las cadenas).

Si React puede permitirnos tener la capacidad de elegir cómo pasamos los datos a los elementos, al igual que en JavaScript simple, eso me convertiría en el usuario más satisfecho. 😊

Nuevamente, lamento mi elección de palabras allí. Lo pensaré dos veces la próxima vez.

Agregué un comentario al RFC PR para esto. Creo que vale la pena discutirlo, ya que cubre lo que se propone, así como un modelo más simple para inferir de manera confiable un elemento personalizado y sus propiedades. Lo convierte de nuevo en un enfoque híbrido, pero ofrece una forma de integración sin configuración para la mayoría de los casos de uso.

Estoy ansioso por ver esta función implementada en React. Muchas gracias por tus esfuerzos para hacer que React sea grandioso.

¿Alguna actualización sobre este RFC?

Las bibliotecas de elementos personalizados se están volviendo realmente buenas y me encantaría usarlas en mi aplicación React. ¿Alguna noticia sobre esto todavía? Envolver elementos personalizados y encadenar su contenido para luego analizarlos nuevamente es una solución bastante impracticable considerando que Vue y Angular manejan componentes de forma nativa con facilidad

¿Alguna actualización sobre este tema?

A mí también me encantaría usar bibliotecas de elementos personalizados sin recurrir a hacks. Me encantaría que se resolviera este problema.

@ mgolub2 El equipo de React no se preocupa por lo que quiere la comunidad web. Los componentes web son ahora un estándar ampliamente admitido, y no hay ningún tipo de señales del equipo para admitir este estándar, después de 2 años.

Hola a todos, comencé una solicitud de extracción para solucionar este problema cambiando dos líneas: https://github.com/facebook/react/pull/16899

Esto permite que el autor de un elemento personalizado haga algo como lo siguiente:

class MyElement extends HTMLElement {
  setAttribute(name, value) {
    // default to existing behavior with strings
    if (typeof value === 'string')
      return super.setAttribute(name, value)

    // but now a custom element author can decide what to do with non-string values.
    if (value instanceof SomeCoolObject) { /*...*/ }
  }
}

Hay muchas variaciones sobre cómo se vería setAttribute método

React team, puede argumentar que los autores de elementos personalizados no deberían hacer eso, porque en algunos casos omiten el manejo de atributos nativos del DOM (por ejemplo, cuando los valores no son cadenas). Si tiene esa opinión, eso no significa que deba obstaculizar lo que los autores de elementos personalizados pueden hacer con sus elementos __ personalizados __.

React no debería opinar sobre cómo se utilizan las API DOM existentes. React es una herramienta para manipular DOM, y no deberíamos opinar sobre lo que podemos hacer con el DOM, solo de qué manera los datos fluyen hacia el DOM. Por "la forma en que los datos fluyen al DOM" me refiero a la ruta que toman los datos para llegar allí, sin mutación en los datos (convertir los objetos de un autor en cadenas es mutar los datos del autor).

¿Por qué quiere mutar los datos del autor? ¿Por qué no puede simplemente asumir que la persona que desea manipular el DOM sabe qué datos pasar al DOM?

@trusktr Creo que esto se discutió en https://github.com/facebook/react/issues/10070

Realmente advertiría a la gente que no le diga a los autores de elementos personalizados que anulen un método integrado como setAttribute . No creo que esté destinado a que nosotros le hagamos un parche. cc @gaearon

Es inofensivo anular setAttribute en subclases como esa (eso no es un parche de mono). Pero como mencioné, ¿por qué React tiene que dictar eso, si ese no es necesariamente el trabajo de la biblioteca React? Queremos usar React para manipular DOM (sin obstáculos).

Si tengo que usar el.setAttribute() manualmente para aumentar el rendimiento, eso también empeora la experiencia del desarrollador.

No creo que el equipo de React esté salvando a muchas personas de un gran peligro al convertir cualquier cosa pasada en setAttribute en cadenas.

Estoy de acuerdo en que otra solución puede ser mejor. Por ejemplo, actualizar la especificación JSX para tener una nueva sintaxis, pero parece que lleva mucho tiempo.

¿Qué pierde la comunidad si eliminamos la conversión automática de cadenas? ¿Qué pierde el equipo de React?

El equipo de React podría mejorar la situación más tarde, con una mejor solución ...

¿Por qué no al menos simplemente darnos una opción para omitir la cadena? ¿Es eso algo que podría estar dispuesto a considerar?

Realmente advertiría a la gente que no le diga a los autores de elementos personalizados que anulen un método integrado como setAttribute .

¿Puede darnos una buena razón de por qué?

Esto parece una pista falsa. "Convencer a todos los que escriben elementos personalizados para que adjunten un método con un comportamiento específico a su elemento" no es una solución a este problema, que se trata de cómo establecer propiedades en elementos DOM.

@trusktr

¿Puede darnos una buena razón de por qué?

Los componentes web son un estándar. Por lo tanto, los derivados del estándar también deben cumplir con los estándares.

Sin embargo, anular setAttribute no se ajusta a esta condición: crea un truco solo para React, mientras que hay muchos otros marcos que funcionan con componentes web listos para usar. Por lo tanto, necesitarían consumir el truco de React incluso cuando no funcionen con React en absoluto. No creo que sea la solución adecuada.

A continuación, es bien sabido que parchear los métodos estándar es un enfoque incorrecto. Anular setAttribute cambia el comportamiento original que puede confundir a los usuarios finales cuando intentan usarlo. Todos esperan que el estándar funcione como estándar, y el elemento personalizado no es una excepción porque hereda el comportamiento HTMLElement . Y aunque podría funcionar con React, crea una trampa para todos los demás usuarios. Por ejemplo, cuando los componentes web se utilizan sin un marco, setAttribute se pueden llamar mucho. Dudo que los desarrolladores de elementos personalizados acepten disparar su pie con este enfoque.

Personalmente, creo que algún tipo de contenedor de React parece mucho más prometedor.

Hola amigos, mientras tanto, mientras esperamos, creé una calza para envolver su componente web en React https://www.npmjs.com/package/reactify-wc

import React from "react";
import reactifyWc from "reactify-wc";

// Import your web component. This one defines a tag called 'vaadin-button'
import "@vaadin/vaadin-button";

const onClick = () => console.log('hello world');

const VaadinButton = reactifyWc("vaadin-button");

export const MyReactComponent = () => (
  <>
    <h1>Hello world</h1>
    <VaadinButton onClick={onClick}>
      Click me!
    </VaadinButton>
  </>
)

Espero que esto te resulte útil

(Esta es mi primera incursión en OSS, y una de las primeras fuentes abiertas de algo fuera de mi oficina. Las críticas constructivas son más que bienvenidas 😄)

Hola amigos, mientras tanto, mientras esperamos, creé una calza para envolver su componente web en React https://www.npmjs.com/package/reactify-wc

import React from "react";
import reactifyWc from "reactify-wc";

// Import your web component. This one defines a tag called 'vaadin-button'
import "@vaadin/vaadin-button";

const onClick = () => console.log('hello world');

const VaadinButton = reactifyWc("vaadin-button");

export const MyReactComponent = () => (
  <>
    <h1>Hello world</h1>
    <VaadinButton onClick={onClick}>
      Click me!
    </VaadinButton>
  </>
)

Espero que esto te resulte útil

(Esta es mi primera incursión en OSS, y una de las primeras fuentes abiertas de algo fuera de mi oficina. Las críticas constructivas son más que bienvenidas 😄)

Gran envoltorio :)

También vale la pena mencionar que la creación de componentes web con Stencil soluciona este problema con React: https://stenciljs.com/docs/faq#can -data-be-pass-to-web-components-

@matsgm Me alegra que la construcción con Stencil haya funcionado para usted. Sin embargo, como advertencia, en nuestra experiencia, Stencil ha demostrado no funcionar bien con otros marcos de componentes web, específicamente Polymer, y nos ha molestado media docena de otros problemas entre sus herramientas de compilación, soporte y funcionalidad general. Su experiencia puede ser diferente :)

Solo intento ponerme al día con este hilo. Cual es la solucion final?

En cierto modo, soy un gran admirador de la definición explícita de attr, prop, event en lugar de heurísticas mágicas que pueden ser muy confusas.

Por ejemplo, snabbdom utiliza un espacio de nombres explícito de nivel superior, esto hace que sea fácil saber qué va y dónde. No hay manipulación de cadenas que sea más rápida de ejecutar, por ejemplo, recortar el sufijo de onClick => click sigue siendo un éxito de rendimiento.

<input attrs={{placeholder: `heyo`}} style={{color: `inherit`}} class={{hello: true, world: false}} on={{click: this.handleClick}} props={{value: `blah`}} />
attr = (attr, val) => elem.setAttribute(attr, val);
prop = (prop, val) => elem[prop] = val;
on  = (event, handler) => elem.addEventListener(event, handler)
style = (prop, val) => elem.style[prop] = val;
class = (name, isSet) => isSet ? elem.classList.add(name) : elem.classList.remove(val)
dataset = (key, val) => elem.dataset[key] = val;

Deseo el espacio de nombres compatible con JSX con sintaxis de puntos. Esto significa que los accesorios serían el espacio de nombres predeterminado y podríamos simplemente escribir

<div tabIndex={-1} attr.title={"abcd"} on.click={handler} style.opacity={1} class.world={true} />`

fyi @sebmarkbage ^

const whatever = 'Whatever';
const obj = { a: 1, b: 2 };
const reactComponent = (props) => (
<div>
  ...
  <custom-element attr="{whatever}" someProp={obj} />
  { /* double quotes for attributes */ }
  { /* no quotes for properties */ }
</div>
);

Esto se puede lograr modificando el analizador React JSX Pragma.

La opción adicional es mantener propeties (o props para los fanáticos de Reacción) como una palabra designada para pasar la propiedad

Dado que la versión 17.0 se lanzó hoy, ¿podríamos actualizar este problema para reflejar el estado de cuándo se abordará?

Sí. Originalmente planeamos React 17 para ser un lanzamiento con muchas más obsoletas eliminadas (y con nuevas características). Sin embargo, no es muy realista que las bases de código antiguas actualicen todo su código, y es por eso que hemos decidido lanzar React 17 que solo se enfoca en un único cambio significativo (los eventos se adjuntan al elemento raíz en lugar del documento ). Esto es para que las aplicaciones antiguas puedan permanecer en React 17 para siempre y solo actualizar partes de ellas a 18+.

Sé que es frustrante que hayamos estado diciendo que esta característica en particular entraría en 17, pero notará que casi todas las cosas que planeamos originalmente para 17 también se movieron a 18. 17 es un lanzamiento especial de trampolín. Lo que nos permite hacer cambios de frenado más agresivos en 18. Incluir potencialmente este si hay un camino claro a seguir.

No estoy seguro de cuál es el consenso más reciente de la comunidad de WC sobre cuál de estas opciones es preferible. Sin embargo, es muy útil tenerlos todos escritos (grandes apoyos para @robdodson por hacer ese trabajo). Tengo curiosidad por saber si las opiniones de la gente sobre estas opciones han evolucionado desde que se escribió este hilo, y si hay alguna información nueva que pueda ayudarnos a elegir la dirección.

No creo que la comunidad de WC haya cambiado su opción preferida, que sigue siendo la opción 3. @developit puede hablar más sobre cómo Preact es compatible con elementos personalizados, lo que también podría ser interesante para React. Para obtener una descripción general de cómo los marcos son compatibles con el paso de datos (complejos) a elementos personalizados, https://custom-elements-everywhere.com/ tiene todos los detalles.

Tenga en cuenta que en https://github.com/vuejs/vue/issues/7582 , Vue eligió usar un sigilo y eligieron "." como prefijo (no la "@" de Glimmer).

En https://github.com/vuejs/vue/issues/7582#issuecomment -362943450, @trusktr sugirió que la implementación de SSR más correcta sería no representar las propiedades sigilizadas como atributos en el HTML de SSR, y en su lugar, configúrelos como propiedades a través de JS durante la hidratación.

Creo que es bastante improbable que introduzcamos una nueva sintaxis JSX solo por el bien de esta característica en particular.

También hay una cuestión de si vale la pena hacer la opción (3) si es posible que haya una versión más genérica de la opción (5) sobre la mesa. Es decir, la opción (5) podría ser un mecanismo de bajo nivel para declarar nodos React personalizados de bajo nivel con comportamiento personalizado de montaje / actualización / desmontaje / hidratación. Ni siquiera es específico de los elementos personalizados per se (aunque sería uno de los casos de uso).

En lugar de introducir una sintaxis JSX completamente nueva para esta función específica, ¿qué tal si se introduce un JSX más general y se utiliza para definir propiedades personalizadas?

Propuse agregar la sintaxis de propiedad calculada de ECMAScript a JSX hace mucho tiempo (facebook / jsx # 108) y creo que sería una adición útil a la sintaxis en general.

Si la sintaxis de propiedad calculada estuviera disponible, dejaría abierta la posibilidad de definir propiedades utilizando la sintaxis de propiedad calculada y los símbolos o cadenas de prefijos.

Por ejemplo:

import {property} from 'react';
// ...
<custom-img [property('src')]="corgi.jpg" [property('hiResSrc')]="[email protected]" width="100%">

@dantman ¿Cómo

No creo que nadie en la comunidad de WC quiera la opción 5.

Realmente no es diferente a la práctica actual de parchear la escritura de un contenedor React personalizado para elementos personalizados. La gran desventaja de ese enfoque es que impone una carga al autor del componente para React en casos especiales o al consumidor del componente a los elementos personalizados de casos especiales, ninguno de los cuales debería ser necesario.

¿Qué piensas sobre la renderización e hidratación del servidor? Si no hay una configuración explícita, ¿qué heurística es deseable? ¿Ha habido una consolidación en la forma en que esto se suele hacer en la comunidad de WC? Releí este RFC y no parece profundizar en los detalles sobre este tema. Pero es bastante crucial, especialmente a medida que React avanza hacia un mayor énfasis en la representación del servidor (ya que a menudo se lo critica con razón por valores predeterminados demasiado centrados en el cliente).

@gaearon No conozco lo suficiente sobre hidratación y propiedades personalizadas para saber qué se requiere, esta es principalmente una propuesta de sintaxis.

La idea general es que property(propertyName) podría, dependiendo de cómo desee implementarlo, generar una cadena con prefijo (por ejemplo, '__REACT_INTERNAL_PROP__$' + propertyName ) o crear un símbolo y guardar una asociación "símbolo => propertyName" en un mapa.

Lo último podría ser problemático si necesita comunicar ese mapa al cliente.

Sin embargo, para mi comprensión aproximada, las propiedades no son algo que pueda manejar en el servidor y la hidratación implica el código del cliente, por lo que no estoy seguro de cuál es el plan para eso. En cuanto a mi propuesta, probablemente se pueda adaptar a los planes que tengas para solucionar ese problema. Si hay algo que planea hacer con las propiedades en el servidor, entonces puede hacerlo cuando vea una de las propiedades creadas por property .

Como autor de una biblioteca de componentes web, la opción 5 no es una opción conveniente. Quiero crear elementos personalizados sin tener que definir explícitamente un esquema para cada marco y componente. Y muchos autores de elementos personalizados simplemente no harán esto, lo que empujará la carga sobre el desarrollador de React.

Nadie gana con la opción 5. 😕

@claviska ¿Tiene alguna opinión sobre cómo la renderización y la hidratación del servidor deberían funcionar con enfoques más implícitos?

@gaearon SSR se subdivide en dos situaciones: aquellas en las que el elemento personalizado admite SSR y aquellas en las que no.

Para los elementos que no admiten SSR, la configuración de propiedades en el servidor no importa. A excepción de las propiedades reflectantes integradas como id y className , se pueden eliminar y solo escribir en el cliente.

Para los elementos que sí son compatibles, las propiedades de SSR serán importantes, pero solo para que puedan desencadenar cualquier resultado que causen en DOM. Esto requiere una instancia de elemento o algún tipo de proxy / suplente SSR, y algún protocolo para comunicar el estado DOM de SSR. Todavía no existe una interfaz común del lado del servidor para este proceso, por lo que realmente no hay nada que hacer aquí por ahora. Los autores de componentes web y los mantenedores de bibliotecas deberán resolver algunas cosas antes de que React pueda construir puentes allí.

En el trabajo de mi equipo en SSR, nos hemos integrado con React parcheando createElement y usando dangerouslySetInnerHTML . Creo que ese es el nivel de integración / experimentación en el que estaremos por un tiempo. Espero que en algún momento pronto podamos converger en algunos protocolos de usuario para la interoperabilidad dentro de los sistemas SSR. Hasta entonces, es perfectamente seguro y prudente tener propiedades de elementos personalizados y compatibilidad con eventos sin _deep_ SSR. La etiqueta del elemento todavía estaría SSR como un elemento secundario de un componente de React como lo es hoy.

A excepción de las propiedades reflectantes integradas como id y className, se pueden eliminar y solo escribir en el cliente.

¿Puedes ayudarme a entender cómo funciona esto? Por ejemplo, digamos que hay <github-icon iconname="smiley" /> . ¿Quiere decir que el SSR solo debería incluir <github-icon /> en la respuesta HTML, y luego React establecerá domNode.iconname = ... durante la hidratación? En ese caso, no estoy seguro de entender qué sucede si la implementación del elemento personalizado se carga antes de que ocurra la hidratación de React. ¿Cómo sabrá la implementación github-icon qué icono representar si iconname no existe en el HTML?

Todavía no existe una interfaz común del lado del servidor para este proceso, por lo que realmente no hay nada que hacer aquí por ahora. Los autores de componentes web y los mantenedores de bibliotecas deberán resolver algunas cosas antes de que React pueda construir puentes allí.

Tengo curiosidad por saber si hay algo en particular que debería suceder para que la comunidad forme un consenso aquí. ¿React es el que lo está reteniendo? Entiendo mucho la frustración asociada con que este tema esté abierto desde 2017. Por otro lado, han pasado tres años y creo que está diciendo que este consenso aún no se ha formado. ¿Cuáles son los requisitos previos para que suceda? ¿Es solo una cuestión de más experimentación?

@gaearon si la implementación del elemento personalizado se carga antes de que se produzca la hidratación de React, asignará el valor predeterminado del atributo iconname . ¿Qué quieres decir con if _iconname_ does not exist in the HTML ? Si el tipo HTMLElement no tiene un atributo definido, lo ignorará. una vez que se cargue la definición del elemento personalizado, extenderá el tipo HTMLElement y definirá iconname y podrá reaccionar a un nuevo valor que se pasa.

Estoy tratando de entender esto desde la perspectiva de un usuario. Estamos renderizando una página en el servidor. Tiene un icono en medio de un texto. Ese icono se implementa como un elemento personalizado. ¿Cuál es la secuencia de cosas que debe experimentar el usuario final?

Mi entendimiento hasta ahora es que:

  1. Ven el resultado de procesamiento inicial. Incluirá todo el marcado normal, pero no verán el elemento personalizado <github-icon> en absoluto. Presumiblemente sería un agujero, como un div vacío. Por favor corrígeme si estoy equivocado (?). Su implementación aún no se ha cargado.

  2. La implementación del elemento personalizado <github-icon> carga y se registra. Si lo entiendo correctamente, este es el proceso llamado "actualización". Sin embargo, aunque su JS está listo, todavía no puede mostrar nada porque no hemos incluido iconname en el HTML. Por lo tanto, no tenemos la información sobre qué ícono representar. Esto se debe a que dijimos anteriormente que "las propiedades no integradas se pueden eliminar del HTML". Entonces, el usuario todavía ve un "agujero".

  3. Reacciona y se carga el código de la aplicación. Ocurre la hidratación. Durante la hidratación, React establece la propiedad .iconname = según la heurística. El usuario puede ver el icono ahora.

¿Es este un desglose correcto para el caso en el que la implementación de JS del elemento personalizado se carga primero? ¿Es este el comportamiento deseable para la comunidad de WC?

@gaearon sí, eso es lo que esperaría que sucediera en esta situación.

Por otro lado, han pasado tres años y creo que estás diciendo que este consenso aún no se ha formado.

No estoy diciendo que no se haya alcanzado el consenso. Estoy diciendo que no necesita preocuparse por _deep_ SSR en este momento, y no debe permitir que preocupaciones bastante vagas al respecto bloqueen la interoperabilidad más básica del lado del cliente que se puede lograr y ser extremadamente útil en este momento.

No he visto esta distinción antes (profunda frente a superficial), así que déjame intentar reafirmarla para comprobar si te entendí.

Por SSR "profundo", creo que te refieres a SSR similar a cómo funciona en los componentes de React. Es decir, renderizar un CE produciría una salida que se puede mostrar antes de que se cargue la lógica de ese CE. Esto es algo que está diciendo que no deberíamos preocuparnos por apoyar ahora. Eso tiene sentido para mi.

Por SSR "no profundo", creo que está diciendo que simplemente colocamos la etiqueta CE en HTML. Sin preocuparse de lo que se resuelva. Eso también tiene sentido para mí. Sin embargo, mi pregunta era sobre _este_ flujo, no sobre la SSR "profunda".

En particular, https://github.com/facebook/react/issues/11347#issuecomment -713230572 describe una situación en la que incluso cuando _ya tenemos_ la lógica CE descargada, todavía no podemos mostrar nada al usuario hasta la hidratación. Se desconocen sus propiedades hasta que se carga el código del cliente JS de toda la aplicación y se produce la hidratación. Estoy preguntando si este es realmente el comportamiento deseado.

No creo que sea una preocupación vaga desde mi perspectiva. No quiero malinterpretar la solicitud de función. Entonces, obtener la semántica exacta especificada me ayudaría a evaluar esta propuesta. Por ejemplo, una preocupación práctica es que React en realidad no difiere atributos / propiedades en la producción durante la hidratación hoy porque ninguno de los componentes incorporados lo necesita. Pero es algo que podríamos agregar para este caso particular si realmente fuera necesario.

@gaearon

Creo que https://github.com/facebook/react/issues/11347#issuecomment -713230572 podría no ser el mejor caso de ejemplo en el que se necesita esta función.

Al menos en mi experiencia, establecer propiedades en lugar de atributos no es todo lo necesario para tipos más simples como cadenas, números o valores booleanos.

El caso que describe podría lograrse fácilmente con solo usar iconname como atributo directamente y seguiría siendo en gran medida el mismo resultado final renderizado sin esperar la hidratación.

losLa implementación del elemento personalizado se carga y se registra. Si lo entiendo correctamente, este es el proceso llamado "actualización". Sin embargo, aunque su JS está listo, todavía no puede mostrar nada porque no hemos incluido el nombre del icono en el HTML. Por lo tanto, no tenemos la información sobre qué ícono representar. Esto se debe a que dijimos anteriormente que "las propiedades no integradas se pueden eliminar del HTML". Entonces, el usuario todavía ve un "agujero".

En cuanto a lo que mencionas aquí, dependiendo de cómo esté implementado el componente podrías evitar este problema de ver un "agujero".

Eso podría lograrse simplemente estableciendo valores predeterminados en este caso o aceptando parte o incluso la mayor parte del contenido a través de ranuras para que el contenido se muestre incluso antes de que se actualice el componente.


Creo que esta característica sería más útil para usar componentes más complejos que tienen propiedades que son objetos, matrices, funciones, etc.

Tomemos como ejemplo el componente de desplazamiento virtual lit-virtualizer .

Para que esto funcione correctamente, se requiere una matriz items y una función renderItem e incluso puede configurar opcionalmente un elemento scrollTarget , todo lo cual solo se puede configurar en React usando refs ahora.

Para un caso como este, probablemente estaría cargando el contenido con algún tipo de paginación o carga diferida de todos modos, por lo que no tener ningún contenido hasta el paso de hidratación en un caso SSR podría no ser tan problemático.

@gaearon Tenga en cuenta que los elementos personalizados son un estándar DOM y, por lo tanto, no existe per se una "comunidad WC" en el sentido de que todos los que usan elementos personalizados los usan de la misma manera. Los elementos personalizados se integran a su manera para cada desarrollador, ya que son inherentemente una primitiva de bajo nivel sobre la que la gente se basa.

Dicho esto, con el tiempo han surgido numerosos patrones y todos los marcos populares (excepto React) implementan alguna solución para brindar compatibilidad con este estándar DOM. Como puede ver en https://custom-elements-everywhere.com/ , todos los marcos / bibliotecas (excepto React) han elegido diferentes opciones. En este número, las opciones elegidas se enumeran como opciones 1, 2 y 3. No existe ningún marco / biblioteca que implemente actualmente la opción 4 o 5 y no creo que sea deseable implementarlas.

Por lo tanto, propongo que React siga el ejemplo de los otros marcos / bibliotecas y elija una opción que haya demostrado funcionar, por ejemplo, entre las opciones 1-3. No creo que React necesite reinventar la rueda aquí eligiendo la opción 4 o 5, de la que no tenemos datos es una solución mantenible a largo plazo para React o aquellos que se basan en los estándares DOM.

Entonces, por el proceso de eliminación (con los comentarios vinculados a continuación), dado que la opción 3 no se hará a JSX y las opciones 4 y 5 han sido marcadas como indeseables por la gente de WC en este hilo, estamos atrapados con 1 o 2 .

Y dado que 1 parece que tiene inconvenientes insostenibles, ¿parece que la Opción 2 es la indicada? ¿Simplemente sigue cómo lo hace Preact?


No hay un marco / biblioteca que implemente actualmente la opción 4 o 5 y no creo que sea deseable implementarlos.

( @TimvdLippe en https://github.com/facebook/react/issues/11347#issuecomment-713474037)

Creo que es bastante improbable que introduzcamos una nueva sintaxis JSX solo por el bien de esta característica en particular.

( @gaearon en https://github.com/facebook/react/issues/11347#issuecomment-713210204)

Eso tendría sentido para mí, sí. ¡Gracias por el resumen! 😄

Al menos en mi experiencia, establecer propiedades en lugar de atributos no es todo lo necesario para tipos más simples como cadenas, números o valores booleanos. El caso que describe podría lograrse fácilmente simplemente usando el nombre de icono como un atributo directamente y seguiría siendo en gran medida el mismo resultado final renderizado sin esperar la hidratación.

No lo estoy siguiendo del todo. Mi escenario hipotético fue una respuesta a https://github.com/facebook/react/issues/11347#issuecomment -713223628 que sonaba como una sugerencia de que no deberíamos emitir atributos en el HTML generado por el servidor en absoluto, excepto id y class . No sé qué significa "usar el nombre de icono como atributo". ¿Está diciendo que le gustaría ver el HTML generado por el servidor _incluir_ otros atributos? Esto parece contradecir lo que se acaba de decir antes. Creo que realmente ayudaría si cada respuesta presentara una imagen completa porque de lo contrario seguiremos en círculos. Mi pregunta para ti es:

  1. ¿Qué se serializa exactamente en el HTML generado por el servidor en el escenario que describí?
    una. En caso de que <github-icon iconname="smiley" />
    B. En el caso que mencionaste, por ejemplo, <github-icon icon={{ name: "smiley" }} />
  2. Qué DOM API se llama en el cliente durante la hidratación en cualquiera de esos casos

¡Gracias!

¿Se consideró una opción para crear un shim, reemplazando React.createElement() con una implementación personalizada, que hace el mapeo de propiedades adentro?

Ejemplo de uso:

/** <strong i="8">@jsx</strong> h */
import { h } from 'react-wc-jsx-shim';

function Demo({ items }) {
   return <my-custom-list items={items} />
}

Proyecto de implementación:

export function h(element, props, children) {
   if(typeof element === 'string' && element.includes('-')) {
      return React.createElement(Wrapper, { props, customElementName: element }, children)
   }
   return React.createElement(element, props, children);

}

function Wrapper({customElementName, props}) {
   const ref = React.useRef();
   React.useEffect(() => {
      for(const prop in props) {
         ref.current[prop] = props[prop];
      }
   });
   return React.createElement(customElementName, { ref, ...props });
}

Sé que esta no es una función incorporada, pero creo que debería desbloquear a los usuarios con una sobrecarga muy baja.

@ just-boris eso es básicamente lo que hacen las aplicaciones o bibliotecas ahora, con un parche createElement o un contenedor. También debe excluir los nombres de propiedad en mayúsculas y minúsculas especiales en React. No creo que haya una lista exportada, por lo que solo codifican una lista de denylist como ['children', 'localName', 'ref', 'elementRef', 'style', 'className'] .

Debido a que está basado en referencias, las propiedades no se establecen en el servidor. Por eso llamo vaga la preocupación. No hay forma de establecer propiedades en React, por lo que todo está roto en este momento. La solución alternativa disponible ya solo funciona en el cliente. Si se incorpora una forma de establecer propiedades en el cliente y no en el servidor, no arregla o rompe repentinamente SSR.

En realidad, estaría más preocupado por los eventos que por SSR. React no tiene una forma de agregar controladores de eventos, y eso es un gran agujero en la interoperabilidad con las API DOM básicas.

¿Se consideró una opción para crear un shim, reemplazando React.createElement () con una implementación personalizada, que asigna propiedades en el interior?

Como lo mencionó Rob en el número original, he experimentado con esto en el pasado a través de https://github.com/skatejs/val , por lo que tener un pragma JSX personalizado que envuelva React.createElement es viable, posiblemente Solución de corto plazo.

También tengo la rama WIP de Skate que implementa lo que se ha denominado SSR profundo y superficial, específicamente para React. El aprendizaje de este trabajo hasta ahora es que no puede tener un solo contenedor JSX para todas las bibliotecas si desea hacer SSR profundo porque cada biblioteca depende de sus propias API y algoritmos para hacerlo. (Para el contexto, Skate tiene un elemento central personalizado que forma parte de su biblioteca y pequeñas capas creadas para integrar cada biblioteca popular en el mercado para hacer que el consumo y el uso sean consistentes en todos los ámbitos).


Ya no opero mucho en código abierto, y tampoco uso Twitter, así que siéntete libre de tomar mi opinión con un grano de sal.

La Opción 1 parece que existe como abogado del diablo para la Opción 2.

La opción 2 parece el camino a seguir. Está cerca de lo que ha hecho Preact, por lo que hay un precedente y la posibilidad de un estándar comunitario (que creo que sería fantástico hacer de todos modos; JSX ha demostrado que esto puede funcionar), y también es un movimiento pragmático para React al ser fácil de actualizar. (a diferencia de la Opción 1).

La opción 3 se ve bien en el papel, pero creo que logísticamente sería mucho más difícil que la opción 2 debido a la sobrecarga cognitiva, la documentación alrededor de dicha sobrecarga y las incógnitas con SSR.

Como se mencionó, originalmente había presentado la opción 4, pero después de reflexionar, ya no estoy seguro de que me guste. Puede cambiarlo para que sea más opcional, donde especifique explícitamente props y events (en lugar de attrs ), pero incluso entonces la Opción 2 requiere menos conocimiento de los detalles de implementación y no hay nada que nadie tenga que cambiar para adoptarlo.

La opción 5 parece que estaríamos ignorando el problema central por completo.


Aparte, estoy muy feliz de ver la tracción en esto y espero que les haya ido bien.

Si hay una posible solución en el área de usuarios, ¿puede ser consolidada y recomendada por la comunidad por el momento?

Sería mucho más fácil convencer al equipo central de React para que agregue una función si ya existe como un módulo de terceros popular y viable.

Si hay una posible solución en el área de usuarios, ¿puede ser consolidada y recomendada por la comunidad por el momento?

No creo que esto sea algo que pueda corregirse [prolijamente] desde el exterior. La mayoría de las soluciones de espacio de usuario son envoltorios como este y este . Sin modificar React, no puedo pensar en una alternativa razonable a este enfoque, y la envoltura definitivamente no es una solución ideal. 😕

Tenga en cuenta que en vuejs / vue # 7582, Vue eligió usar un sigilo y eligieron "." como prefijo (no la "@" de Glimmer).

No tengo un enlace a una declaración autorizada sobre esto (y tal vez alguien más cercano a Vue pueda confirmar explícitamente), pero parece que Vue se ha movido a la Opción 2 a partir de Vue 3.0, lo que significa que se comporta de manera similar a Preact. (Consulte este número para conocer el contexto).

Debido a que está basado en referencias, las propiedades no se establecen en el servidor. Por eso llamo vaga la preocupación. No hay forma de establecer propiedades en React, por lo que todo está roto en este momento. La solución alternativa disponible ya solo funciona en el cliente. Si se incorpora una forma de establecer propiedades en el cliente y no en el servidor, no arregla o rompe repentinamente SSR.

¿Puede responder a las preguntas específicas en https://github.com/facebook/react/issues/11347#issuecomment -713514984? Siento como si estuviéramos hablando entre nosotros. Solo quiero comprender todo el comportamiento previsto, de una manera específica. Qué se establece cuándo y qué se serializa en qué caso.

Como desarrollador aleatorio de Preact + WC que tropezó aquí, quería señalar que la "Opción 2" no es solo un _cambio revolucionario_, sino también una _ solución incompleta_ (Preact solo puede interactuar declarativamente con un subconjunto de componentes web válidos).

Incluso como usuario actual de la heurística, eso suena poco deseable a largo plazo.


La heurística de Preact ("opción 2") no puede establecer propiedades que tengan un significado especial en React (clave, hijos, etc.) o Preact (cualquier cosa que comience con on ).

Es decir, después de renderizar debajo de JSX:

<web-component key={'key'} ongoing={true} children={[1, 2]} />

las propiedades key , ongoing y children serán todas undefined (o el valor predeterminado que sea) en la instancia creada de web-component .

Además, al contrario que el OP, esta opción también es un cambio importante. La mayoría de los componentes web (por ejemplo, hechos con lit-element ) ofrecen la capacidad de pasar datos enriquecidos serializados a través de atributos, con el propósito de ser utilizados con HTML puro. Dichos componentes se pueden renderizar desde React así

<web-component richdata={JSON.stringify(something)} />

que se romperá si se elige la "opción 2". No estoy seguro de cuán común es el uso de componentes web en React, pero definitivamente hay algunas bases de código que lo hacen, es menos eficiente que las referencias, pero también más terso.

Finalmente, el autor de Web Component es libre de adjuntar semánticas sutilmente diferentes al atributo y propiedad del mismo nombre. Un ejemplo sería un componente web que imita el comportamiento del elemento nativo input , tratando el atributo value como valor inicial y solo observando los cambios en la propiedad value . Dichos componentes no se pueden interactuar completamente a través de jsx con el enfoque heurístico, y también podrían experimentar una ruptura sutil si se introdujera.

Creo que es bastante improbable que introduzcamos una nueva sintaxis JSX solo por el bien de esta característica en particular.

¿Qué pasa con el uso de espacios de nombres? https://github.com/facebook/jsx/issues/66 propone agregar un espacio de nombres react para key y ref . En la misma línea, ¿algo como react-dom sería un espacio de nombres aceptable para identificar las propiedades del DOM? Usando el ejemplo de la opción 3 del OP, eso sería:

<custom-img react-dom:src="corgi.jpg" react-dom:hiResSrc="[email protected]" width="100%">

Ese no es el más ergonómico. Solo dom sería mejor, pero no estoy seguro de si React quiere crear espacios de nombres que no comiencen con react . Además, la mala ergonomía en esto no es el fin del mundo, si la idea es que la configuración como propiedades debería ser el caso menos común. Usar atributos es mejor, ya que proporciona paridad con SSR. Establecer como propiedades es para cuando los atributos son problemáticos (como para pasar un objeto o para propiedades no respaldadas por atributos), y es precisamente para esas propiedades problemáticas que no podemos SSR a los atributos de todos modos, y necesitaríamos hidratarlos. a través de JS.

una _solución incompleta_

(Preact todavía solo puede interactuar declarativamente con un subconjunto de componentes web válidos)

@brainbadger, ¿puedes también editar tu comentario de arriba para incluir qué es realmente este subconjunto? P.ej. ¿Qué componentes web / elementos personalizados se ven afectados (con ejemplos)? ¿Y cómo se ven afectados exactamente?

@karlhorky Agregué una explicación. Perdón por ser innecesariamente vago.

Respecto a los eventos de Componentes Web. ¿Existe todavía algún consenso sobre cómo evitar futuros bloqueos mediante la colisión de nombres de eventos?

Esto también es un problema para los atributos y propiedades, ya que se pueden agregar nuevos atributos a HTMLElement como todos estos: https://developer.mozilla.org/en-US/docs/Web/HTML/Global_attributes Parece que hay un par de los recién agregados también, por lo que todavía se están agregando nuevos.

Creo que estrictamente hablando, debería tener que usar un - en el nombre de cualquier atributo personalizado o evento personalizado.

Cualquier otra cosa es un compromiso que sopesa los riesgos de que ese componente impida que una especificación futura use un nombre agradable.

Sin embargo, los eventos me ponen más nervioso porque no es solo un riesgo de que ese componente tenga un comportamiento incompatible. Si el nuevo evento burbujea, activará todos los componentes web existentes en esa ruta de árbol.

Si se requiriera que los eventos y atributos personalizados usen un - en su nombre, esto podría usarse como una heurística para determinar qué usar. Luego daríamos un caso especial a los integrados. Así es como sabemos si es un elemento personalizado en primer lugar.

Entonces, las propiedades podrían usarse como conveniencia para proporcionar una mano corta más agradable. Al menos esos pueden ocultar las propiedades integradas y no colisionar con propiedades futuras.

Creo que, estrictamente hablando, deberías usar un - en el nombre de cualquier atributo personalizado o evento personalizado.

La especificación del elemento personalizado no incluye tales restricciones, por lo que hacerlas cumplir en React afectará significativamente la interoperabilidad.

No se debe esperar que los autores de elementos personalizados se suscriban a los requisitos arbitrarios de un marco. Imagínese cada marco haciendo esto con diferentes opiniones y convenciones. Esto frustra el propósito de usar componentes web. 😕

Idealmente, el marco debería adaptarse para adaptarse al estándar.

En efecto. setAttribute() y dispatchEvent() son solo API no específicas de componentes web que han permitido nombres arbitrarios desde sus inicios. Cualquier elemento puede tener cualquier atributo y activar cualquier evento; es solo una verdad básica del DOM.

Sin embargo, el espacio data- nombres

No sugeriría que solo React agregue esto, pero que la comunidad cambie a esta convención para ayudar a proteger el espacio de nombres futuro. Creo que es una lástima que este problema no se tome en serio. Pero quizás la web esté condenada a solo agregar nombres incómodos a las nuevas API.

Me gusta la idea de excluir simplemente los no incorporados de SSR y prefiero las propiedades durante la hidratación porque con el DOM de sombra declarativo el contenido real podría expandirse en el interior.

Concretamente, creo que eso causará problemas para los enfoques actuales que la gente usa con SSR + AMP, ya que usa atributos y se puede asumir que el tiempo de ejecución de AMP ya se ha cargado.

Suponiendo que la puerta no está cerrada en la opción 3, tiene una ventaja significativa sobre la opción 2, que no se enumera en los contras anteriores: el problema que encontré con la opción 2 es que si las bibliotecas de componentes web se cargan de forma asíncrona , es posible que preact no saber, para un elemento desconocido, que una "propiedad" será una propiedad. Dado que no encuentra la propiedad existente en el elemento desconocido, toma por defecto un atributo. Una solución es no renderizar ese componente hasta que se carguen las bibliotecas de componentes web dependientes, lo que no es muy elegante (o ideal en cuanto al rendimiento). Dado que mi grupo usa asp.net, no node.js, nunca he explorado SSR realmente, por lo que las observaciones a continuación son especulativas.

Es un buen gesto, creo, por parte del equipo de React, sugerir ir más allá de lo que admiten otros marcos (que yo sepa), en lo que respecta a SSR, permitiendo que las propiedades del objeto se transmitan durante la renderización inicial, lo que podría servir al usuario mejor.

No sé si estoy afirmando lo obvio o no, pero creo que hay un poco de un consenso con los componentes web que para algunas propiedades, es conveniente a veces establecer el valor inicial del atributo a través de una cadena JSON , envuelto entre comillas simples.

Sin embargo, AMP usa otra convención: usa script type = application.json para hacer esto, que es una alternativa razonable (pero detallada).

Pero siguiendo con el enfoque de atributos:

La biblioteca de componentes web puede analizar el atributo o pasar el valor a través de una propiedad.

Así que para evitar retrasos en la hidratación sería conveniente configurar:

<github-icon icon='{"name":"smiley"}'></github-icon>

durante SSR. Luego, github-icon podría hacer lo suyo de inmediato, hacer internamente un JSON.parse del atributo y no tener que esperar a que React pase el valor del objeto (¿más completo?).

Así que ahora tenemos una situación complicada: si se renderiza en el servidor, queremos que "icono" se trate como un atributo, pero poder pasar el valor como un objeto, que idealmente el mecanismo SSR se encadenaría en el atributo. En el cliente, queremos poder pasar el objeto directamente y no pasar por la sobrecarga de secuenciar y analizar.

Por supuesto, algunas propiedades son objetos que no se pueden secuenciar / analizar, por lo que esto no se aplica, y esas propiedades tendrían que esperar a que se complete la hidratación.

Si todas las propiedades y atributos siguieron la convención de nomenclatura preferida del equipo de React (quizás): todos los atributos tuvieran guiones y todas las propiedades fueran nombres compuestos usando camelCase, tal vez la existencia de un guión podría ayudar a distinguir entre atributos y propiedades:

<github-icon my-icon={myStringifiedProperty} myIcon={myObjectProperty}></github-icon>

El problema es que nada en la sintaxis anterior indica qué hacer en el servidor frente al cliente, e idealmente podríamos usar un enlace para ambos, y React sería lo suficientemente inteligente como para descubrir cuál usar.

React podría simplemente asumir que si una clave de atributo / propiedad es un caso camel, siempre se debe pasar como un objeto en el lado del cliente, y una serialización JSON-stringified del objeto si se establece durante SSR. Del mismo modo, los guiones en la clave podrían (con seguridad, creo) asumir que es un atributo y simplemente pasar el .toString () del valor. Pero creo que esto supone demasiado. Y no admitir atributos y propiedades de una sola palabra, que se considera HTML válido si los atributos se aplican a un componente web que extiende HTMLElement, sería demasiado restringido. Estoy a favor de que el W3C emita una lista de nombres de atributos "reservados" que podría usar en el futuro, similar a las palabras clave reservadas para JS, y marcos que encuentren formas de advertir a los desarrolladores si están usando incorrectamente un nombre de atributo reservado.

Pero creo que la opción 3 es el mejor enfoque. Sin embargo, si se puede mejorar, como sugiere @gaearon , para una experiencia de usuario aún mejor, sería genial.

Mi sugerencia sería:

<github-icon icon={myDynamicIcon}/>

significa atributo (toString ()).

<github-icon icon:={myDynamicIcon}/>

significaría: durante SSR, ignorar, pero vincular al cliente a la propiedad del objeto (después de hidratar).

Ahora, ¿qué pasa con el escenario (¿algunos de los?) El equipo de React está interesado en resolver? Mi primer pensamiento fue solo otro sigilo, como dos dos puntos:

<github-icon icon::={myDynamicIcon}/> //Not my final suggestion!

significaría, durante SSR, JSON.stringificar la propiedad, establecer como un atributo en el HTML renderizado y pasar como un objeto en el cliente cuando la propiedad se vincula a los cambios después de la hidratación.

Esto deja la difícil situación de qué hacer con los nombres compuestos. Es decir, si establecemos:

<github-icon iconProps::={myDynamicIconProp}/>  //Not my final suggestion!

No era controvertido en el pasado que el atributo correspondiente para el nombre de propiedad iconProps debería ser icon-props.

Quizás esto se ha vuelto más controvertido, debido al espectro de que algunos de estos componentes web podrían, sin cambios, integrarse en la plataforma, donde los atributos no pueden tener guiones (pero las propiedades pueden ser camelCase). Que yo sepa, todavía no existe un elemento nativo que permita pasar objetos complejos a través de la deserialización JSON, pero no me sorprendería si surgiera la necesidad en el futuro. Entonces, ¿cómo sabría React cuándo insertar guiones o no?

La única sugerencia (¿fea?) Que se me ocurre es:

<github-icon icon-props:iconProps={myDynamicIconProp}/>

lo que significa que, en el servidor, use atributos icon-props después de serializar, y en el cliente use la propiedad iconProps, pasando objetos directamente.

Otro beneficio potencial (¿posibilidad remota?) De la notación más detallada, que permite un par híbrido de atributo / propiedad, es este: podría ser un poco más rápido establecer repetidamente las propiedades de un elemento nativo, en lugar del atributo correspondiente, especialmente para propiedades que no son cadenas. Si es así, ¿React usa actualmente atributos en el servidor y propiedades en el cliente? Si no es así, ¿se debe al mismo problema de las dificultades de traducción de nombres, que resolvería esta notación?

@bahrus creo que se puede simplificar

<my-element attr='using-quotes' property={thisIsAnObject} />

Creo que sería mejor ilustrar el problema con un ejemplo concreto.

El elemento Formulario HTML tiene varios atributos. Los que tienen "nombres compuestos" no parecen seguir un patrón de nomenclatura coherente; por lo general, se mantiene en minúsculas, sin separadores, pero a veces hay un separador de guiones, por ejemplo, "accept-charset", a veces no. - "no validar".

El nombre de propiedad JS correspondiente tampoco encaja en un patrón universal agradable: acceptCharset, noValidate. El atributo noValidate property / novalidate es un booleano, por lo que en el cliente, sería un desperdicio (imagino) hacer myForm.setAttribute ('novalidate', '') en lugar de myForm.noValidate = true.

Sin embargo, en el servidor, no tiene sentido establecer myForm.noValidate = true, ya que queremos enviar una representación de cadena del DOM, por lo que debemos usar el atributo:

<form novalidate={shouldNotValidate}>

En realidad, no me queda claro que React / JSX tenga una forma universal de establecer un atributo booleano de forma condicional , confiando, quizás, en una tabla de búsqueda fija y estática, que parece (?) Demasiado rígida . Si puedo ser tan audaz, parece que esta es otra área que React / JSX podría mejorar, para ser totalmente compatible con la forma (desordenada) en que funciona el DOM, ¿como parte de este RFC?

¿Cómo podemos representar todos estos escenarios, de modo que el desarrollador tenga control total sobre el servidor (a través de atributos) y el cliente (a través de propiedades), sin sacrificar el rendimiento? En este caso de un booleano como novalidate, propondría:

<form novalidate:noValidate?={shouldNotValidate}/>

Si React apoyara completamente el DOM, por desordenado que sea, de la manera más eficiente posible, creo que sería de gran ayuda para admitir elementos personalizados, que probablemente tampoco sean muy consistentes en los patrones de nombres.

Me parece que agregar soporte para serializar opcionalmente propiedades de objetos a atributos a través de JSON.stringify en el servidor sería una gran ventaja adicional para React y los componentes web.

¿O me estoy perdiendo algo? ( @eavichay , ¿cómo sugeriría que se representen mejor estos escenarios, sin seguir su ejemplo?).

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