React: RFC: Planen Sie benutzerdefinierte Elementattribute/Eigenschaften in React 18

Erstellt am 24. Okt. 2017  ·  129Kommentare  ·  Quelle: facebook/react

Dies soll #7249 adressieren. Das Dokument skizziert die Vor- und Nachteile verschiedener Ansätze, die React verwenden könnte, um Attribute und Eigenschaften von benutzerdefinierten Elementen zu behandeln.

Inhaltsverzeichnis/Zusammenfassung

  • Hintergrund
  • Vorschläge

    • Option 1: Nur Eigenschaften festlegen

    • Vorteile



      • Leicht verständlich/umsetzbar


      • Vermeidet Konflikte mit zukünftigen globalen Attributen


      • Nutzt die Vorteile des benutzerdefinierten Elements "Upgrade"


      • Benutzerdefinierte Elemente werden wie jede andere React-Komponente behandelt



    • Nachteile



      • Möglicherweise eine bahnbrechende Veränderung


      • Benötige Ref, um das Attribut zu setzen


      • Nicht klar, wie serverseitiges Rendering funktionieren würde



    • Option 2: Eigenschaften – falls verfügbar

    • Vorteile



      • Nicht brechende Änderung



    • Nachteile



      • Entwickler müssen die Heuristik verstehen


      • Das Zurückgreifen auf Attribute kann mit zukünftigen Globals in Konflikt geraten



    • Option 3: Eigenschaften mit einem Siegel differenzieren

    • Vorteile



      • Nicht brechende Änderung, die Entwickler aktivieren können


      • Ähnlich wie andere Bibliotheken mit Attributen/Eigenschaften umgehen


      • Das System ist explizit



    • Nachteile



      • Es ist eine neue Syntax


      • Nicht klar, wie serverseitiges Rendering funktionieren würde



    • Option 4: Hinzufügen eines Attributobjekts

    • Vorteile



      • Das System ist explizit


      • Das Erweitern der Syntax kann auch Probleme mit der Ereignisbehandlung lösen



    • Nachteile



      • Es ist eine neue Syntax


      • Es kann eine bahnbrechende Veränderung sein


      • Es kann eine größere Änderung sein als jeder der vorherigen Vorschläge



    • Option 5: Eine API zum Konsumieren benutzerdefinierter Elemente

    • Vorteile



      • Das System ist explizit


      • Nicht brechende Änderung


      • Idiomatisch zu reagieren



    • Nachteile



      • Könnte viel Arbeit für ein komplexes Bauteil sein


      • Kann Bündelgröße aufblähen


      • Config muss mit der Komponente Schritt halten



Hintergrund

Wenn React versucht, Daten an ein benutzerdefiniertes Element zu übergeben, verwendet es immer HTML-Attribute.

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

Da Attribute in Zeichenfolgen serialisiert werden müssen, führt dieser Ansatz zu Problemen, wenn die übergebenen Daten ein Objekt oder ein Array sind. In diesem Szenario erhalten wir so etwas wie:

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

Die Problemumgehung hierfür besteht darin, ein ref zu verwenden, um die Eigenschaft manuell festzulegen.

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

Diese Problemumgehung fühlt sich etwas unnötig an, da die meisten heute ausgelieferten benutzerdefinierten Elemente mit Bibliotheken geschrieben sind, die automatisch JavaScript-Eigenschaften generieren, die alle ihre exponierten Attribute unterstützen. Und jeder, der ein benutzerdefiniertes Vanilla-Element von Hand erstellt, wird ermutigt, diese Vorgehensweise ebenfalls zu befolgen . Wir möchten, dass die Laufzeitkommunikation mit benutzerdefinierten Elementen in React idealerweise standardmäßig JavaScript-Eigenschaften verwendet.

Dieses Dokument enthält einige Vorschläge, wie React aktualisiert werden könnte, um dies zu erreichen.

Vorschläge

Option 1: Nur Eigenschaften festlegen

Anstatt zu entscheiden, ob eine Eigenschaft oder ein Attribut festgelegt werden soll, könnte React immer Eigenschaften für benutzerdefinierte Elemente festlegen. React würde vorher NICHT prüfen, ob die Eigenschaft für das Element vorhanden ist.

Beispiel:

<x-foo bar={baz}>

Der obige Code würde dazu führen, dass React die Eigenschaft .bar des Elements x-foo auf den Wert von baz .

Für camelCased-Eigenschaftsnamen könnte React denselben Stil verwenden, den es heute für Eigenschaften wie tabIndex .

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

Vorteile

Leicht verständlich/umsetzbar

Dieses Modell ist einfach, explizit und passt zu Reacts "JavaScript-centric API to the DOM" .

Jedes Element, das mit Bibliotheken wie Polymer oder Skate erstellt wurde, generiert automatisch Eigenschaften, um ihre exponierten Attribute zu unterstützen. Diese Elemente sollten alle mit dem obigen Ansatz "einfach funktionieren". Entwickler, die Vanilla-Komponenten von Hand erstellen, werden ermutigt, Attribute mit Eigenschaften zu unterstützen, da dies widerspiegelt, wie moderne (dh keine Sonderlinge wie <input> ) HTML5-Elemente ( <video> , <audio> usw.) Wurden umgesetzt.

Vermeidet Konflikte mit zukünftigen globalen Attributen

Wenn React ein Attribut für ein benutzerdefiniertes Element festlegt, besteht immer das Risiko, dass eine zukünftige HTML-Version ein ähnlich benanntes Attribut ausliefert und Dinge kaputt macht. Dieses Problem wurde mit den Spezifikationsautoren diskutiert, aber es gibt keine klare Lösung für das Problem. Das vollständige Vermeiden von Attributen (außer wenn ein Entwickler explizit eines mit ref festlegt) kann dieses Problem umgehen, bis die Browser eine bessere Lösung finden.

Nutzt die Vorteile des benutzerdefinierten Elements "Upgrade"

Benutzerdefinierte Elemente können auf der Seite träge aktualisiert werden und einige PRPL-Muster basieren auf dieser Technik. Während des Upgrade-Prozesses kann ein benutzerdefiniertes Element auf die von React übergebenen Eigenschaften zugreifen – selbst wenn diese Eigenschaften vor dem Laden der Definition festgelegt wurden – und sie zum Rendern des Anfangszustands verwenden.

Benutzerdefinierte Elemente werden wie jede andere React-Komponente behandelt

Wenn React-Komponenten Daten aneinander weitergeben, verwenden sie bereits Eigenschaften. Dies würde nur dazu führen, dass sich benutzerdefinierte Elemente genauso verhalten.

Nachteile

Möglicherweise eine bahnbrechende Veränderung

Wenn ein Entwickler von Hand benutzerdefinierte Vanilla-Elemente erstellt hat, die nur über eine Attribute-API verfügen, muss er seinen Code aktualisieren oder seine App wird kaputt gehen. Die Lösung wäre, ein ref zu verwenden, um das Attribut zu setzen (siehe unten).

Benötige Ref, um das Attribut zu setzen

Durch die Änderung des Verhaltens, sodass Eigenschaften bevorzugt werden, müssen Entwickler ref , um explizit ein Attribut für ein benutzerdefiniertes Element festzulegen.

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

Dies ist nur eine Umkehrung des aktuellen Verhaltens, bei dem Entwickler ref benötigen, um eine Eigenschaft festzulegen. Da Entwickler selten Attribute für benutzerdefinierte Elemente festlegen müssen, scheint dies ein vernünftiger Kompromiss zu sein.

Nicht klar, wie serverseitiges Rendering funktionieren würde

Es ist nicht klar, wie dieses Modell benutzerdefinierten Elementen des serverseitigen Renderings zugeordnet werden würde. React könnte annehmen, dass die Eigenschaften ähnlich benannten Attributen zugeordnet sind, und versuchen, diese auf dem Server zu setzen, aber dies ist alles andere als kugelsicher und würde möglicherweise eine Heuristik für Dinge wie camelCased-Eigenschaften -> Attribute mit Bindestrich erfordern.

Option 2: Eigenschaften – falls verfügbar

Zur Laufzeit könnte React versuchen zu erkennen, ob eine Eigenschaft in einem benutzerdefinierten Element vorhanden ist. Wenn die Eigenschaft vorhanden ist, wird sie von React verwendet, andernfalls wird auf das Setzen eines Attributs zurückgegriffen. Dies ist das Modell, das Preact verwendet, um mit benutzerdefinierten Elementen umzugehen.

Pseudocode-Implementierung:

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

Mögliche Schritte:

  • Wenn ein Element über eine definierte Eigenschaft verfügt, verwendet React diese.

  • Wenn ein Element eine undefinierte Eigenschaft hat und React versucht, ihm primitive Daten (String/Zahl/Boolean) zu übergeben, verwendet es ein Attribut.

    • Alternative: Warnen und nicht einstellen.
  • Wenn ein Element eine undefinierte Eigenschaft hat und React versucht, ihm ein Objekt/Array zu übergeben, wird es als Eigenschaft festgelegt. Dies liegt daran, dass some-attr="[object Object]" nicht nützlich ist.

    • Alternative: Warnen und nicht einstellen.
  • Wenn das Element auf dem Server gerendert wird und React versucht, ihm einen String/eine Zahl/einen booleschen Wert zu übergeben, wird ein Attribut verwendet.

  • Wenn das Element auf dem Server gerendert wird und React versucht, ihm ein Objekt/Array zu übergeben, wird es nichts tun.

Vorteile

Nicht brechende Änderung

Es ist möglich, ein benutzerdefiniertes Element zu erstellen, das nur Attribute als Schnittstelle verwendet. Dieser Authoring-Stil wird NICHT empfohlen, kann aber trotzdem vorkommen. Wenn sich ein Autor eines benutzerdefinierten Elements auf dieses Verhalten verlässt, wäre diese Änderung für ihn nicht schädlich.

Nachteile

Entwickler müssen die Heuristik verstehen

Entwickler könnten verwirrt sein, wenn React ein Attribut anstelle einer Eigenschaft festlegt, je nachdem, wie sie ihr Element geladen haben.

Das Zurückgreifen auf Attribute kann mit zukünftigen Globals in Konflikt geraten

Sebastian äußerte Bedenken, dass die Verwendung von in zum Prüfen auf Vorhandensein einer Eigenschaft in einem benutzerdefinierten Element versehentlich eine Eigenschaft in der Oberklasse (HTMLElement) erkennen könnte.

Es gibt auch andere potenzielle Konflikte mit globalen Attributen, die zuvor in diesem Dokument

Option 3: Eigenschaften mit einem Siegel differenzieren

React könnte weiterhin Attribute für benutzerdefinierte Elemente festlegen, aber ein Siegel bereitstellen, mit dem Entwickler stattdessen Eigenschaften explizit festlegen können. Dies ähnelt dem von Glimmer.js verwendeten Ansatz .

Glimmer-Beispiel:

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

Im obigen Beispiel gibt das @-Siegel an, dass src und hiResSrc mithilfe von Eigenschaften Daten an das benutzerdefinierte Element übergeben und width in eine Attributzeichenfolge serialisiert werden sollen.

Da React-Komponenten bereits Daten mithilfe von Eigenschaften aneinander weitergeben, müssten sie das Siegel nicht verwenden (obwohl es funktionieren würde, wenn sie es täten, wäre es nur redundant). Stattdessen würde es in erster Linie als explizite Anweisung verwendet, um Daten mithilfe von JavaScript-Eigenschaften an ein benutzerdefiniertes Element zu übergeben.

h/t an @developit von Preact, um diesen Ansatz vorzuschlagen :)

Vorteile

Nicht brechende Änderung, die Entwickler aktivieren können

Alle bereits vorhandenen React + benutzerdefinierten Element-Apps würden weiterhin genau so funktionieren, wie sie es bisher getan haben. Entwickler konnten wählen, ob sie ihren Code aktualisieren wollten, um den neuen Siegelstil zu verwenden.

Ähnlich wie andere Bibliotheken mit Attributen/Eigenschaften umgehen

Ähnlich wie bei Glimmer verwenden sowohl Angular als auch Vue Modifikatoren, um zwischen Attributen und Eigenschaften zu unterscheiden.

Vue-Beispiel:

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

Winkelbeispiel:

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

Das System ist explizit

Entwickler können React genau sagen, was sie wollen, anstatt sich auf eine Heuristik wie den Eigenschaften-wenn-Verfügbarkeit- Ansatz zu verlassen.

Nachteile

Es ist eine neue Syntax

Entwickler müssen lernen, wie man es benutzt, und es muss gründlich getestet werden, um sicherzustellen, dass es abwärtskompatibel ist.

Nicht klar, wie serverseitiges Rendering funktionieren würde

Sollte das Siegel zu einem ähnlich benannten Attribut wechseln?

Option 4: Hinzufügen eines Attributobjekts

React könnte eine zusätzliche Syntax hinzufügen, die es Autoren ermöglicht, Daten explizit als Attribute zu übergeben. Wenn Entwickler dieses Attribute-Objekt nicht verwenden, werden ihre Daten mithilfe von JavaScript-Eigenschaften übergeben.

Beispiel:

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

Diese Idee wurde ursprünglich von @treshugart , Autor von Skate.js, vorgeschlagen und in der val- Bibliothek implementiert.

Vorteile

Das System ist explizit

Entwickler können React genau sagen, was sie wollen, anstatt sich auf eine Heuristik wie den Eigenschaften-wenn-Verfügbarkeit- Ansatz zu verlassen.

Das Erweitern der Syntax kann auch Probleme mit der Ereignisbehandlung lösen

Hinweis: Dies ist außerhalb des Rahmens dieses Dokuments, aber vielleicht erwähnenswert :)

Problem #7901 fordert, dass React sein synthetisches Ereignissystem umgeht, wenn deklarative Ereignishandler zu benutzerdefinierten Elementen hinzugefügt werden. Da benutzerdefinierte Elementereignisnamen beliebige Zeichenfolgen sind, können sie in beliebiger Weise großgeschrieben werden. Um das synthetische Ereignissystem heute zu umgehen, müssen Sie auch eine Heuristik entwickeln, um Ereignisnamen von JSX auf addEventListener abzubilden.

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

Wenn die Syntax jedoch erweitert wird, um Attribute zuzulassen, könnte sie auch erweitert werden, um auch Ereignisse zuzulassen:

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

In diesem Modell wird der Variablenname als Ereignisname verwendet. Es ist keine Heuristik erforderlich.

Nachteile

Es ist eine neue Syntax

Entwickler müssen lernen, wie man es benutzt, und es muss gründlich getestet werden, um sicherzustellen, dass es abwärtskompatibel ist.

Es kann eine bahnbrechende Veränderung sein

Wenn Komponenten bereits auf Eigenschaften namens attrs oder events angewiesen sind, könnten sie beschädigt werden.

Es kann eine größere Änderung sein als jeder der vorherigen Vorschläge

Für React 17 kann es einfacher sein, eine inkrementelle Änderung vorzunehmen (wie bei einem der vorherigen Vorschläge) und diesen Vorschlag als etwas zu positionieren, das für eine spätere, größere Überarbeitung in Betracht gezogen werden sollte.

Option 5: Eine API zum Konsumieren benutzerdefinierter Elemente

Dieser Vorschlag wurde von @sophiebits und @gaearon vom React-Team angeboten

React könnte eine neue API zum Konsumieren benutzerdefinierter Elemente erstellen, die das Verhalten des Elements einem Konfigurationsobjekt zuordnet.

Pseudocode-Beispiel:

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

Der obige Code gibt eine Proxy-Komponente zurück, XFoo , die je nach der von Ihnen bereitgestellten Konfiguration weiß, wie Daten an ein benutzerdefiniertes Element übergeben werden. Sie würden diese Proxy-Komponente in Ihrer App verwenden, anstatt das benutzerdefinierte Element direkt zu verwenden.

Anwendungsbeispiel:

<XFoo someRichDataProp={...} />

Vorteile

Das System ist explizit

Entwickler können React genau das gewünschte Verhalten mitteilen.

Nicht brechende Änderung

Entwickler können sich für die Verwendung des Objekts entscheiden oder das aktuelle System weiter verwenden.

Idiomatisch zu reagieren

Diese Änderung erfordert keine neue JSX-Syntax und ähnelt eher anderen APIs in React. PropTypes zum Beispiel (obwohl es in ein eigenes Paket verschoben wird) hat einen etwas ähnlichen Ansatz.

Nachteile

Könnte viel Arbeit für ein komplexes Bauteil sein

Das Papiereingabeelement von Polymer hat 37 Eigenschaften, so dass es eine sehr große Konfiguration erzeugen würde. Wenn Entwickler viele benutzerdefinierte Elemente in ihrer App verwenden, kann dies vielen Konfigurationen entsprechen, die sie schreiben müssen.

Kann Bündelgröße aufblähen

In Bezug auf den obigen Punkt fallen jetzt für jede benutzerdefinierte Elementklasse die Kosten für ihre Definition + ihre Konfigurationsobjektgröße an.

Hinweis: Ich bin mir nicht 100% sicher, ob das stimmt.

Config muss mit der Komponente Schritt halten

Jedes Mal, wenn die Komponente eine Nebenversionsrevision durchführt, die eine neue Eigenschaft hinzufügt, muss auch die Konfiguration aktualisiert werden. Das ist nicht schwierig, aber es erhöht die Wartung. Wenn Konfigurationen aus der Quelle generiert werden, ist dies vielleicht weniger aufwendig, aber das kann bedeuten, dass ein neues Tool erstellt werden muss, um Konfigurationen für jede Webkomponentenbibliothek zu generieren.

cc @sebmarkbage @gaearon @developit @treshugart @justinfagnani

DOM Discussion

Hilfreichster Kommentar

Hey Leute, während wir warten, habe ich in der Zwischenzeit ein Shim zum Umhüllen Ihrer Webkomponente in React erstellt 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>
  </>
)

Ich hoffe, das erweist sich als hilfreich

(Dies ist mein erster Ausflug in OSS und einer der ersten Open-Sourcing von etwas außerhalb meines Büros. konstruktive Kritik ist mehr als willkommen )

Alle 129 Kommentare

Entschuldigung für das lange Lesen, aber ich wollte sicherstellen, dass ich jede Option gründlich erkundete. Ich möchte die Dinge nicht zu sehr mit meiner eigenen Meinung verzerren, aber wenn ich in der Lage wäre zu wählen, würde ich mich für Option 3 entscheiden.

Option 3 ist abwärtskompatibel, deklarativ und explizit. Es besteht keine Notwendigkeit, eine Fallback-Heuristik zu pflegen, und andere Bibliotheken bieten bereits ähnliche Siegel/Modifikatoren.

Entschuldigung für das lange Lesen, aber ich wollte sicherstellen, dass ich jede Option gründlich erkundete. Ich möchte die Dinge nicht zu sehr mit meiner eigenen Meinung verzerren, aber wenn ich in der Lage wäre zu wählen, würde ich mich für Option 3 entscheiden.
Option 3 ist abwärtskompatibel, deklarativ und explizit. Es besteht keine Notwendigkeit, eine Fallback-Heuristik zu pflegen, und andere Bibliotheken bieten bereits ähnliche Siegel/Modifikatoren.

Ich liege zwischen Option 2 und Option 3. Ich denke, dass React in der Vergangenheit sehr gut mit Verhaltens- und API-Änderungen umgegangen ist. Die Einführung von Warnungen und Links zu Dokumenten kann Entwicklern helfen, zu verstehen, was unter der Haube passiert.

Option 3 sieht aufgrund ihres deklarativen Charakters attraktiv aus, während neue Entwickler beim Lesen des JSX-Codes sofort wissen, was React beim Rendern des Elements tun wird.

Kommentare zu Option 2

Entwickler könnten verwirrt sein, wenn React ein Attribut anstelle einer Eigenschaft festlegt, je nachdem, wie sie ihr Element geladen haben.

Müssen die Verbraucher eines benutzerdefinierten Elements diese Unterscheidung verstehen? Oder ist das nur dem Autor des benutzerdefinierten Elements wichtig? Es scheint, als müsste der Autor des Elements Attribute für alles, was in HTML verwendet wird (da dies der einzige Weg ist, auf dem Daten aus der HTML-Nutzung weitergegeben werden), und Eigenschaften handhaben, wenn er komplexe Werte oder das Get/Set von Eigenschaften aus DOM unterstützen möchte. Es ist sogar möglich, dass ein Autor etwas ursprünglich als Attribut implementiert hat und später eine Eigenschaft mit demselben Namen hinzufügt, um flexiblere Datentypen zu unterstützen und die Eigenschaft dennoch mit einem in den Attributen gespeicherten Wert zu unterstützen.

Namenskollisionen mit zukünftigen HTMLElement-Attributen und -Eigenschaften scheint eine Schwäche in den Webkomponenten-Standards im Allgemeinen zu sein, da dies unabhängig vom Bindungsansatz zu Fehlern führen kann.

Wenn ein Element eine undefinierte Eigenschaft hat und React versucht, ihm ein Objekt/Array zu übergeben, wird es als Eigenschaft festgelegt. Dies liegt daran, dass some-attr="[object Object]" nicht nützlich ist.

Es erscheint verwirrend, je nach Wert unterschiedlich zu binden. Wenn der Autor des Elements keinen Eigenschafts-Getter/-Setter zum Behandeln des Werts angegeben hat, würde das Festlegen der Eigenschaft dazu führen, dass sich das Element so verhält, als wäre der Wert nie angegeben worden, was möglicherweise schwieriger zu debuggen ist.

Kommentare zu Option 3

Ein weiterer potenzieller Nachteil von Option 3 besteht darin, dass der Verbraucher des benutzerdefinierten Elements wissen muss, ob das Element etwas als Eigenschaft oder als Attribut implementiert hat. Wenn Sie eine Mischung aus React-Komponenten und benutzerdefinierten Elementen verwenden, kann es verwirrend sein, React-Requisiten mit einer Syntax und benutzerdefinierte Elementeigenschaften mit einer anderen Syntax festzulegen.

Müssen die Verbraucher eines benutzerdefinierten Elements diese Unterscheidung verstehen? Oder ist das nur dem Autor des benutzerdefinierten Elements wichtig?

Ich bezweifle, dass es sich tatsächlich um ein großes Problem handelt, da der Elementautor, wie Sie darauf hingewiesen haben, ein Attribut und eine Eigenschaft für den zugrunde liegenden Wert definieren und Daten von beiden akzeptieren sollte. Ich würde auch hinzufügen, dass sie das Attribut und die Eigenschaft synchron halten sollten (so dass das Festlegen eines das andere festlegt).

Namenskollisionen mit zukünftigen HTMLElement-Attributen und -Eigenschaften scheint eine Schwäche in den Webkomponenten-Standards im Allgemeinen zu sein, da dies unabhängig vom Bindungsansatz zu Fehlern führen kann.

Ich stimme zu, aber ich bin mir nicht sicher, ob dies etwas ist, das React in seiner Bibliothek umgehen muss. Es fühlt sich an wie ein Problem, das als Teil der benutzerdefinierten Elementspezifikation gelöst werden muss. Ich kann sehen, ob wir es im Rahmen des bevorstehenden TPAC-Standardtreffens diskutieren können.

Ich sollte hinzufügen, dass dies für Eigenschaften nicht _so_ schlecht ist, da die elementdefinierte Eigenschaft die zukünftige Eigenschaft, die zu HTMLElement hinzugefügt wird, überschatten wird. Wenn Sie also Daten als js-Eigenschaft an ein benutzerdefiniertes Element übergeben, würde es weiterhin funktionieren. Das Hauptproblem scheint bei Attributen zu liegen, da sie global sind.

Es erscheint verwirrend, je nach Wert unterschiedlich zu binden. Wenn der Autor des Elements keinen Eigenschafts-Getter/-Setter zum Behandeln des Werts angegeben hat, würde das Festlegen der Eigenschaft dazu führen, dass sich das Element so verhält, als wäre der Wert nie angegeben worden, was möglicherweise schwieriger zu debuggen ist.

Für den Fall, dass ein benutzerdefiniertes Element verzögert geladen und "aktualisiert" wird, hat es zunächst undefinierte Eigenschaften. Dies adressiert diesen Anwendungsfall, indem sichergestellt wird, dass diese Elemente weiterhin ihre Daten erhalten und sie diese nach dem Upgrade verwenden können.

Es stimmt, dass dies nicht sehr nützlich wäre, wenn der Autor keinen Getter/Setter für einen Wert definiert. Aber es ist auch nicht sinnvoll, ein my-attr=[object Object] . Und da Sie nicht wissen, ob die Eigenschaft wirklich undefiniert ist oder ob die Definition nur träge geladen wird, scheint es am sichersten, die Eigenschaft festzulegen.

Ein weiterer potenzieller Nachteil von Option 3 besteht darin, dass der Verbraucher des benutzerdefinierten Elements wissen muss, ob das Element etwas als Eigenschaft oder als Attribut implementiert hat.

Ich denke, Sie sitzen heute im Wesentlichen im selben Boot, weil nichts den Autor eines benutzerdefinierten Elements dazu zwingt, ein Attribut anstelle einer Eigenschaft zu definieren. Ich könnte also ein Element mit einer API nur für Eigenschaften haben, die keine Daten vom aktuellen System von React erhält, und ich müsste wissen, dass ich ref , um die js-Eigenschaften direkt festzulegen.

Da benutzerdefinierte Elemente als Primitive gedacht sind, gibt es nichts, was das Erstellen entsprechender Attribute und Eigenschaften erzwingt. Aber wir bemühen uns sehr, dies als Best Practice zu fördern, und alle Bibliotheken, die ich heute kenne, erstellen Backing-Eigenschaften für ihre Attribute.

[bearbeiten]

Wie Sie in Ihrem früheren Punkt gesagt haben:

Es scheint, als müsste der Autor des Elements Attribute für alles, was in HTML verwendet wird (da dies der einzige Weg ist, auf dem Daten aus der HTML-Nutzung weitergegeben werden), und Eigenschaften handhaben, wenn er komplexe Werte oder das Get/Set von Eigenschaften aus DOM unterstützen möchte.

Da Sie nie wissen, wie ein Benutzer versucht, Daten an Ihr Element zu übergeben, benötigen Sie sowieso eine Attribut-Eigenschafts-Korrespondenz. Ich stelle mir vor, wenn Option 3 ausgeliefert würde, würden die meisten Leute einfach alles mit dem Siegel @ binden, weil es am einfachsten wäre. So arbeite ich heute mit benutzerdefinierten Elementen in Vue, da sie einen .prop Modifikator bereitstellen.

es erfordert, dass der Verbraucher des benutzerdefinierten Elements weiß, ob das Element etwas als Eigenschaft oder als Attribut implementiert hat

Das ist nichts, was React meiner Meinung nach befürchten sollte, wie Rob sagte, es liegt in der Verantwortung des Autors des benutzerdefinierten Elements, den Benutzer über die Funktionsweise des Elements zu informieren.

Und es ist tatsächlich die Art und Weise, wie wir es heute tun müssen, denken Sie zum Beispiel an das Element <video> , sagen wir, Sie müssen es stummschalten oder die aktuelle Uhrzeit in einer Komponente ändern.

muted funktioniert als boolesches Attribut

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

Für die aktuelle Zeit müssen Sie ein ref erstellen, das auf das Videoelement zeigt und die Eigenschaft ändern.

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

Erstellen Sie dann einen Ereignishandler, eine Instanzmethode und setzen Sie die Eigenschaft manuell auf das DOM-Element.

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

Wenn Sie darüber nachdenken, bricht es irgendwie das deklarative Modell, das React selbst mit seiner abstrakten API- und JSX-Ebene auferlegt, da es sich bei currentTime eindeutig um einen Zustand in der Wrapper-Komponente handelt Das JSX-Abstraktionsmodell wäre deklarativer und Refs wären nicht nur dafür erforderlich:

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

Mein Punkt ist, dass Sie sich unabhängig davon, ob Sie sich auf native oder benutzerdefinierte Elemente verlassen, auf der Grundlage der Dokumentation auskennen müssen, mit dem Unterschied, dass sie im zweiten Fall vom Autor des benutzerdefinierten Elements stammen sollte.

@cjorasch meine zwei Cent :)

Wenn wir dies von Grund auf neu entwerfen würden, ohne die Abwärtskompatibilität berücksichtigen zu müssen, wäre Option 1 meiner Meinung nach die idiomatischste pro Reacts "JavaScript-centric API to the DOM" .

Könnte dieses Problem in Bezug auf das serverseitige Rendering gelöst werden, indem eine API für Anwendungscode bereitgestellt wird, um React darüber zu informieren, wie benutzerdefinierte Elementeigenschaften Attributen zugeordnet werden können? Ähnlich den Karten, die React bereits für plattformdefinierte Attribute pflegt? Diese API müsste nur einmal pro benutzerdefiniertem Elementnamen aufgerufen werden (nicht für jede Instanz davon) und nur für Eigenschaften, die keiner direkten 1:1-Entsprechung mit ihrem Attribut folgen, was hoffentlich relativ selten sein sollte.

Wenn wir jedoch befürchten, dass dies zu viel von einer bahnbrechenden Änderung ist, dann denke ich, dass Option 3 auch ziemlich ansprechend ist. Wenn das Siegel eine Eigenschaft bezeichnet, würde ich "." vorschlagen, da dies bereits der Eigenschafts-Accessor von JavaScript ist. Ich halte es jedoch für bedauerlich, jede Instanz, in der ein benutzerdefiniertes Element in einer Anwendung verwendet wird, dafür verantwortlich zu machen, zu wissen, wann ein Attribut oder wann eine Eigenschaft verwendet werden soll. Was ich an Option 1 bevorzuge, ist, dass dieser Zuordnungscode von allen JSX-Verwendungen isoliert werden kann, selbst wenn eine Eigenschaft zur Attributzuordnung benötigt wird.

Für den Fall, dass ein benutzerdefiniertes Element verzögert geladen und "aktualisiert" wird, hat es zunächst undefinierte Eigenschaften. Dies adressiert diesen Anwendungsfall, indem sichergestellt wird, dass diese Elemente weiterhin ihre Daten erhalten und sie diese nach dem Upgrade verwenden können.

Vielleicht verstehe ich den Upgrade-Prozess nicht. Elemente haben normalerweise Eigenschaften, die im Klassenprototyp als Getter/Setter definiert sind. Das Überprüfen von propName in element würde true zurückgeben, da der Getter/Setter vorhanden ist, selbst wenn der Eigenschaftswert noch nicht definiert war. Werden während des Upgrades Eigenschaftswerte für eine temporäre Instanz festgelegt und später nach Abschluss des Lazy Load in die tatsächliche Instanz kopiert?

Upgrade ist der Prozess, durch den das benutzerdefinierte Element seine Klasse erhält. Davor ist es keine Instanz dieser Klasse, daher sind die Eigenschaften-Getter/-Setter nicht verfügbar.

@jeremenichelli

muted funktioniert als boolesches Attribut

gerade überprüft und es hat auch eine entsprechende Eigenschaft, obwohl es auf MDN nicht dokumentiert zu sein scheint: P

Für die aktuelle Zeit müssen Sie eine Referenz erstellen, die auf das Videoelement zeigt, und die Eigenschaft ändern.

Ja, gelegentlich werden Sie bei modernen HTML-Elementen auf reine Eigenschaften-APIs stoßen. currentTime mit hoher Häufigkeit aktualisiert, daher wäre es nicht sinnvoll, dies in einem HTML-Attribut wiederzugeben.

Mein Punkt ist, dass Sie sich auf der Grundlage der Dokumentation auskennen müssen, egal ob Sie sich auf native oder benutzerdefinierte Elemente verlassen

Ja, es gibt leider keine einheitliche Regel für Attribute/Eigenschaften. Aber ich denke, im Allgemeinen können Sie sich stark auf Eigenschaften stützen und eine Syntax bereitstellen, damit Entwickler in besonderen Fällen Attribute verwenden können.

@robdodson ja, ich wusste auch von der muted-Eigenschaft 😄 Ich habe diese beiden gerade verwendet, um zu beweisen, dass es bereits _in freier Wildbahn_ keine einheitliche Regel gibt, wie Sie es erwähnt haben.

Wir werden uns auf die Dokumentation sowohl zu nativen als auch zu benutzerdefinierten Elementen verlassen müssen, daher würde ich diese Entscheidung nicht stören.

Beim Schreiben des letzten Code-Snippets hat mir die Eigenschaftsbindung jedoch irgendwie gefallen 💟

@efffulgentsia

Ich halte es jedoch für bedauerlich, jede Instanz, in der ein benutzerdefiniertes Element in einer Anwendung verwendet wird, dafür verantwortlich zu machen, zu wissen, wann ein Attribut oder wann eine Eigenschaft verwendet werden soll.

Ich denke aber, das ist heute schon der Fall. Da die wichtigsten Bibliotheken für benutzerdefinierte Elemente (Polymer, Skate, möglicherweise andere?) automatisch Hintergrundeigenschaften für alle exponierten Attribute erstellen, könnten Entwickler einfach das Siegel für jede Eigenschaft eines benutzerdefinierten Elements verwenden. Es würde wahrscheinlich selten vorkommen, dass sie auf die Verwendung eines Attributs umsteigen müssen.

@cjorasch

AW: Upgrade. Wie @effulgentsia erwähnt, ist es möglich, ein benutzerdefiniertes Element auf der Seite zu haben, aber seine Definition zu einem späteren Zeitpunkt zu laden. <x-foo> ist anfangs eine Instanz von HTMLElement und wenn ich seine Definition später lade, "aktualisiert" es und wird zu einer Instanz der XFoo Klasse. An diesem Punkt werden alle seine Lebenszyklus-Callbacks ausgeführt. Wir verwenden diese Technik im Polymer Starter Kit-Projekt. Etwa so:

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

Im obigen Beispiel laden wir die Definition für my-view2 erst, wenn der Router darauf wechselt.

Es ist durchaus möglich, eine Eigenschaft für das Element festzulegen, bevor es aktualisiert wurde, und sobald die Definition geladen ist, kann das Element diese Daten während eines seiner Lebenszyklus-Callbacks abrufen.

Entwickler könnten das Siegel einfach für jede Eigenschaft eines benutzerdefinierten Elements verwenden

Wenn Entwickler damit beginnen würden, wie würde sich dann die Verwendung einer Eigenschaft unterscheiden, weil Sie "können" und eine Eigenschaft verwenden, weil Sie "müssen"? Und ist das nicht eine Unterscheidung, die für das serverseitige Rendering benötigt wird?

Wenn Entwickler damit beginnen würden, wie würde sich dann die Verwendung einer Eigenschaft unterscheiden, weil Sie "können" und eine Eigenschaft verwenden, weil Sie "müssen"?

Sorry, vielleicht habe ich das falsch formuliert. Ich meinte, dass Entwickler wahrscheinlich das Siegel verwenden würden, weil es das konsistenteste Ergebnis liefern würde. Sie können es verwenden, um primitive Daten oder umfangreiche Daten wie Objekte und Arrays zu übergeben, und es wird immer funktionieren. Ich denke, die Arbeit mit Eigenschaften zur Laufzeit wird im Allgemeinen der Arbeit mit Attributen vorgezogen, da Attribute eher für die Erstkonfiguration verwendet werden.

Und ist das nicht eine Unterscheidung, die für das serverseitige Rendering benötigt wird?

Es kann vorkommen, dass das Siegel auf dem Server auf das Setzen eines Attributs zurückgreift.

Es kann vorkommen, dass das Siegel auf dem Server auf das Setzen eines Attributs zurückgreift.

Ich glaube nicht, dass das funktionieren würde, wenn der Grund für das Siegel darin besteht, dass es sich um eine Eigenschaft handelt, die nicht als Attribut existiert, wie z. B. currentTime von

Unterscheiden Sie die Verwendung einer Eigenschaft, weil Sie "können", von der Verwendung einer Eigenschaft, weil Sie "müssen"

Ich denke, diese Unterscheidung ist wichtig, da es ganz andere Gründe gibt, ein Attribut oder eine Eigenschaft als Optimierung zu verwenden (z Ein Besitz.

Könnte dieses Problem in Bezug auf das serverseitige Rendering gelöst werden, indem eine API für Anwendungscode bereitgestellt wird, um React darüber zu informieren, wie benutzerdefinierte Elementeigenschaften Attributen zugeordnet werden können?

Genauer gesagt schlage ich so etwas vor:

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

Beispiele:

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

Für etwas, das nur eine Eigenschaft sein kann (wobei htmlAttributeName null ist), würde SSR das Rendern überspringen und es dann auf dem Client hydratisieren.

Für etwas, das nur ein Attribut sein kann (wobei domPropertyName null ist), würde React setAttribute() wie derzeit in v16 aufrufen.

Für etwas, das beides sein kann, könnte React die optimale Strategie wählen. Vielleicht bedeutet das, dass man auf der Client-Seite immer als Eigenschaft, auf der Server-Seite aber als Attribut setzt. Vielleicht bedeutet dies die Einstellung als Attribut beim anfänglichen Erstellen des Elements, aber die Einstellung als Eigenschaft beim späteren Patchen von der vdom. Vielleicht bedeutet dies, dass nur als Attribut festgelegt wird, wenn der Wert ein primitiver Typ ist. Im Idealfall sollte React die Strategie als internes Umsetzungsdetail jederzeit ändern können.

Wenn React auf eine Requisite stößt, für die defineCustomElementProp() nicht aufgerufen wurde und die nicht durch die HTML-Spezifikation als globale Eigenschaft oder Attribut definiert ist, kann React eine Standardlogik implementieren. Zum Beispiel vielleicht:

  • In Version 17 BC mit v16 pflegen und als Attribut festlegen.
  • Gehen Sie in Version 18 davon aus, dass es eines von beiden sein kann, und folgen Sie der optimalsten Strategie dafür.

Aber in jedem Fall bleiben die JSX- und props Objekte sauber und in einem einzigen Namespace, genau wie bei React-Komponenten und nicht benutzerdefinierten HTML-Elementen.

Entschuldigung für die übertriebenen Kommentare, aber mir ist ein weiterer Vorteil meines obigen Vorschlags eingefallen, den ich gerne teilen möchte:

Diese ReactDOM.defineCustomElementProp() Aufrufe könnten in einer JS-Datei bereitgestellt werden, die vom Autor des benutzerdefinierten Elements verwaltet wird (im selben Repository, in dem das benutzerdefinierte Element verwaltet/verteilt wird). Es wäre nicht erforderlich für benutzerdefinierte Elemente mit einer strikten 1:1-Entsprechung von Eigenschaft/Attribut, was gemäß der Hintergrunderklärung dieser Ausgabe sowieso die Empfehlung und der Mehrheitsfall ist. Nur Autoren von benutzerdefinierten Elementen, die dieser Empfehlung nicht folgen, müssen die React-Integrationsdatei bereitstellen. Wenn der Autor es nicht bereitstellt (z. B. weil der Autor des benutzerdefinierten Elements sich nicht für React interessiert), könnte die Community von Personen, die dieses benutzerdefinierte Element in React-Apps verwenden, ein zentrales Repository für die Unterbringung dieser Integrationsdatei selbst organisieren.

Ich denke, die Möglichkeit einer solchen Zentralisierung ist einer Lösung vorzuziehen, bei der jeder Benutzer des benutzerdefinierten Elements immer mit einem Siegel explizit sein muss.

Option 3 wäre meine bevorzugte, aber das ist eine große bahnbrechende Änderung ... Was ist mit der Umkehrung? Attribute haben ein Präfix, keine Requisiten?

Siegel in React, ich weiß nicht, was ich davon halten soll. Die JSX-Spezifikation sollte als universell angesehen werden, nicht übermäßig abhängig oder abhängig von Browser-Spezifika, insbesondere nicht von Unregelmäßigkeiten aufgrund der Abwärtskompatibilität. obj[prop] = value ist bedauerlich, dass sich obj.setAttributes(props, value) unterschiedlich verhalten, aber wenn man sich die Browser-API als Ganzes ansieht, ist das keine Überraschung. @ : [] würde ein Detail der Implementierung an die Oberfläche lecken und dem Javascript-zentrierten Ansatz widersprechen. Wenn wir also keine Spezifikation haben, die Folgendes tut, halte ich es für eine schlechte Idee: const data = <strong i="8">@myFunction</strong> // -> "[object Object]"

Wenn ich mich auf eine Webkomponente verlassen muss, würde ich mich freuen, wenn die Semantik von React und JSX versteckt wird und keine Breaking Changes eingeführt werden. Von allen Optionen scheint es mir günstig zu sein, ref => ... an Ort und Stelle zu lassen. ref speziell für den Zugriff auf das Objekt entwickelt. Und zumindest weiß der Entwickler genau, was vor sich geht, es gibt kein Sigil-Leak, auch keine neuen Attribute, die bestehende Projekte zerstören könnten.

@LeeCheneler

Option 3 wäre meine bevorzugte, aber das ist eine große bahnbrechende Änderung ... Was ist mit der Umkehrung? Attribute haben ein Präfix, keine Requisiten?

Warum sollte es eine bahnbrechende Veränderung sein? Das aktuelle Verhalten von Attributen als Standard würde beibehalten. Das Siegel wäre ein Opt-In und Entwickler würden es verwenden, um die Stellen in ihrem Code zu ersetzen, an denen sie derzeit ein ref , um Daten als JS-Eigenschaft an ein benutzerdefiniertes Element zu übergeben.

@drcmda

weder neue Attribute, die bestehende Projekte zerstören könnten.

Können Sie verdeutlichen, was Sie damit meinten?

Zu Ihrer Information für alle, die die Diskussion verfolgen, ich habe den RFC mit einer fünften Option aktualisiert, die von Mitgliedern des React-Teams vorgeschlagen wurde.

@robdodson

ich meinte das:

Option 4: Hinzufügen eines Attributobjekts
Nachteile
Es kann eine bahnbrechende Veränderung sein

Option 5 scheint für uns am sichersten. Dadurch können wir die Funktion hinzufügen, ohne jetzt eine Entscheidung über die „implizite“ API treffen zu müssen, da sich das Ökosystem noch in der Phase des „Herausfindens“ befindet. Wir können es in ein paar Jahren immer wieder besuchen.

Das Papiereingabeelement von Polymer hat 37 Eigenschaften, so dass es eine sehr große Konfiguration erzeugen würde. Wenn Entwickler viele benutzerdefinierte Elemente in ihrer App verwenden, kann dies vielen Konfigurationen entsprechen, die sie schreiben müssen.

Mein Eindruck ist, dass Benutzer von benutzerdefinierten Elementen in React irgendwann sowieso einige benutzerdefinierte Elemente in React-Komponenten einschließen möchten, um app-spezifisches Verhalten/Anpassungen zu erzielen. Es ist eine schöne Migrationsstrategie für diesen Fall , wenn alles bereits eine ist React Komponente, zum Beispiel

import XButton from './XButton';

und das wird zufällig erzeugt von

export default ReactDOM.createCustomElementType(...)

Auf diese Weise können sie eine React-Komponente jederzeit durch eine benutzerdefinierte Komponente ersetzen, die benutzerdefinierte Elemente verwendet (oder sogar nicht verwendet).

Wenn Leute also React-Komponenten an Interop-Punkten erstellen, können wir genauso gut einen mächtigen Helfer dafür bereitstellen. Es ist auch wahrscheinlich, dass Benutzer diese Konfigurationen für benutzerdefinierte Elemente teilen, die sie verwenden.

Und wenn sich das Ökosystem schließlich stabilisiert, können wir einen konfigurationslosen Ansatz verfolgen.

Ich denke, der nächste Schritt hier wäre, einen detaillierten Vorschlag zu schreiben, wie die Konfiguration aussehen sollte, um alle gängigen Anwendungsfälle zu erfüllen. Es sollte für Benutzer von benutzerdefinierten Elementen + React überzeugend genug sein, da wir, wenn es nicht auf allgemeine Anwendungsfälle (wie Ereignisbehandlung) reagiert, in der Schwebe landen werden, in der die Funktion nicht genügend Vorteile bietet, um die Ausführlichkeit auszugleichen .

Aufbauend auf meinem früheren Kommentar , wie wäre es mit:

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

Die Logik wäre dann für jede React-Requisite auf einer XFoo-Instanz:

  1. Wenn eventName für diese Requisite nicht null ist, registrieren Sie sie als Event-Handler, der den Requisitenwert aufruft (von dem angenommen wird, dass es sich um eine Funktion handelt).
  2. Andernfalls, wenn clientseitiges Rendern und propertyName nicht null ist, setzen Sie die Elementeigenschaft auf den Prop-Wert.
  3. Andernfalls, wenn attributeName nicht null ist, setzen Sie das Elementattribut auf den stringifizierten Prop-Wert. Wenn attributeSerializer nicht null ist, verwenden Sie es, um den Requisitenwert zu stringifizieren. Andernfalls tun Sie einfach '' + propValue .

Das Papiereingabeelement von Polymer hat 37 Eigenschaften, so dass es eine sehr große Konfiguration erzeugen würde.

Ich möchte vorschlagen, dass die Konfiguration nur für Ausreißer-Requisiten erforderlich ist. Für jede Requisite auf der XFoo-Instanz, die nicht in der Konfiguration enthalten war, setzen Sie sie standardmäßig auf:

  • wenn der Wert eine Funktion ist:
eventName: the prop name,
  • anders:
propertyName: the prop name,
attributeName: camelCaseToDashCase(the prop name),

Alternativ ist es vielleicht sinnvoll, Ereignisse in einem separaten Namensraum zu speichern. Entfernen Sie in diesem Fall alles, was mit eventName zu tun hat, aus dem letzten Kommentar und lassen Sie Ereignisse stattdessen registrieren als:

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

@gaearon @effulgentsia was

Option 1 würde es dem gelegentlichen Benutzer eines benutzerdefinierten Elements erleichtern, umfangreiche Daten zu übergeben. Ich stelle mir das Szenario vor, in dem ich eine App erstelle und nur ein paar benutzerdefinierte Elemente verwenden möchte. Ich weiß bereits, wie sie funktionieren und bin nicht so investiert, dass ich eine Konfiguration für sie schreiben möchte.

Option 5 wäre für Leute, die so etwas wie Papiereingaben in ihrer gesamten App verwenden möchten und die gesamte API wirklich jedem in ihrem Team zugänglich machen möchten.

Für SSR der Option 1 könnte die Heuristik immer ein Attribut verwenden, wenn auf dem Server gerendert wird. Eine camelCase-Eigenschaft wird in ein Bindestrich-Attribut umgewandelt. Dies scheint ein ziemlich häufiges Muster in Webkomponentenbibliotheken zu sein.

Ich mag die Idee einer Kombination aus Option1 + Option5 sehr. Das bedeutet für die meisten benutzerdefinierten Elemente:

<x-foo prop1={propValue1}>

würde wie erwartet funktionieren: prop1 set als Eigenschaft clientseitig und als (gestricheltes) Attribut serverseitig.

Und die Leute könnten für alles zu Option 5 wechseln, für das das Obige nicht zu ihnen passt.

Es wäre jedoch eine bahnbrechende Änderung gegenüber der Funktionsweise von React 16. Für jeden, der diesen Bruch erlebt (zB wenn er ein benutzerdefiniertes Element mit Attributen verwendet hat, die nicht durch Eigenschaften unterstützt werden), könnte er zu Option 5 wechseln, aber es ist immer noch ein Bruch. Ich überlasse es dem React-Team zu entscheiden, ob das akzeptabel ist.

Ah, das bekomme ich, wenn ich das schnell im Zug lese @robdodson 🤦‍♂️ ... Ich

Option 5 erscheint vernünftig und einfach.

Ich mag, wohin @effulgentsia geht. Gibt es einen Grund, warum es nicht sein kann:

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

Oder ist es sinnvoll, mehrere Typen auf einer einzigen Requisite zu unterstützen?

Ich würde mit diesem Flow jedoch zögern @effulgentsia :

wenn der Wert eine Funktion ist:
eventName: der Requisitenname,
anders:
propertyName: der Prop-Name,
attributeName: camelCaseToDashCase (der Requisitenname),

Ich glaube nicht, dass ich möchte, dass eine Funktionsstütze standardmäßig einem Ereignis entspricht, und ist es sinnvoll, sowohl propertyName als auch attributeName zuzuweisen? Wann möchten Sie, dass beide unterstützt werden, um die obige Frage nachzuahmen? 🙂

@LeeCheneler :

Zitat aus den Option 1-Profis der Problemzusammenfassung:

Jedes Element, das mit Bibliotheken wie Polymer oder Skate erstellt wurde, generiert automatisch Eigenschaften, um ihre exponierten Attribute zu unterstützen. Diese Elemente sollten alle mit dem obigen Ansatz "einfach funktionieren". Entwickler, die Vanilla-Komponenten von Hand erstellen, werden ermutigt, Attribute mit Eigenschaften zu unterstützen, da dies widerspiegelt, wie moderne (dh keine Sonderlinge wie <input> ) HTML5-Elemente ( <video> , <audio> usw.) Wurden umgesetzt.

Aus diesem Grund ist es sinnvoll, sowohl propertyName als auch attributeName zuzuweisen: weil es widerspiegelt, was bei Elementen, die Best Practices befolgen, tatsächlich der Fall ist. Und indem React dies bewusst gemacht wird, kann React je nach Situation entscheiden, welche verwendet werden sollen: beispielsweise die Verwendung von Eigenschaften für das clientseitige Rendering und Attribute für das serverseitige Rendering. Bei benutzerdefinierten Elementen, die nicht der Best Practice folgen und einige Attribute ohne entsprechende Eigenschaften und/oder einige Eigenschaften ohne entsprechende Attribute haben, müsste sich React dessen bewusst sein, damit attributlose Eigenschaften während SSR und Eigenschaft nicht gerendert werden -less-attributes kann beim clientseitigen Rendering mit setAttribute() gesetzt werden.

Bei Ihrem Vorschlag könnte dies möglicherweise durch Bitkombinations-Flags erfolgen, wie zum Beispiel:

propName1: T.Property | T.Attribute,

Dies würde jedoch keine Möglichkeit bieten, auszudrücken, dass sich der Attributname vom Eigenschaftsnamen unterscheidet (z. B. camelCase in dash-case). Es würde auch keine Möglichkeit bieten, auszudrücken, wie ein Rich-Objekt während SSR zu einem Attribut serialisiert wird (das aktuelle Verhalten von "[object Object]" ist nicht nützlich).

Ich glaube nicht, dass ich möchte, dass eine Funktionseigenschaft standardmäßig auf ein Ereignis gesetzt wird

Ja, ich glaube, dem stimme ich auch zu, daher der Folgekommentar . Danke, dass du mein Zögern damit bestätigt hast!

Hier ist ein Gedanke für eine weniger ausführliche Version meines früheren Vorschlags :

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],
  },
});

Und gemäß dem obigen Codekommentar empfehle ich dringend, nicht zu verlangen, dass jede reflektierte Eigenschaft definiert wird, sondern alles, was nicht automatisch als reflektierte Eigenschaft definiert ist, deren Attributname die Version mit Bindestrich ist, vorzugeben.

Macht Sinn @effulgentsia 👍

Ich mag Ihr zweites Beispiel, aber ist es nicht offen für eine kombinatorische Explosion, wenn mehr Typen hinzugefügt werden, ala Events + was auch immer Sinn machen könnte?

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

Obwohl ich vermute, dass Sie ein Ereignis sowieso nicht mit einer Requisite oder einem Attribut mischen möchten 🤔 Attribut und Requisite sind wahrscheinlich die einzigen Dinge, die Sie nachahmen möchten.

Ich denke, hier bietet sich sowohl für die React- als auch für die Web Component-Community die Möglichkeit, sich an einer Best Practice auszurichten. Reagieren Sie hier eine Meinung zu haben, wird einen großen Beitrag dazu leisten, dass Autoren von benutzerdefinierten Elementen aufgrund der weit verbreiteten Akzeptanz und des Gewichts, das ihre Meinungen haben, in die richtige Richtung geleitet werden.

Obwohl ich die Implementierung von Option 4 erstellt habe, muss ich immer Attribute und Ereignisse von Eigenschaften trennen. Im Idealfall würde ich Option 1 bevorzugen. In der Praxis würde ich Option 2 mit einer Notluke bevorzugen.

Option 1 ist ideal, aber es gibt viele Arten von Attributen, die keine entsprechenden Eigenschaften haben (aria / data), daher sind zusätzliche Heuristiken erforderlich, und wenn es Randfälle gibt, in denen Elemente ein Attribut haben können, das a . haben

Option 2 ist vorzuziehen, da es sich um eine nicht brechende Änderung handelt. Es funktioniert in allen Situationen, in denen die benutzerdefinierte Elementdefinition registriert wird, bevor eine Instanz erstellt (oder geladen wird, bevor Eigenschaften festgelegt werden). Stand der Technik für diese Option ist Preact (cc @developit) und hat bisher gut funktioniert. Wenn Sie diesen Weg einschlagen, erhalten Sie eine einigermaßen robuste, bruchsichere Implementierung, die sich in einer erfolgreichen React-Variante bewährt hat und in den meisten Situationen funktioniert. Wenn überhaupt, gibt es React eine kurzfristige Lösung, während bessere Lösungen langfristig bewertet werden.

Für die Situation (Situation s ?), in der es nicht funktioniert - verzögertes Laden von benutzerdefinierten Elementdefinitionen und anderen, die wir nicht behandelt haben - könnte eine Fluchtluke wie die von Incremental DOM implementiert werden. Dies ähnelt dem, was @effulgentsia vorschlägt, würde aber besser auf x Anzahl benutzerdefinierter Elemente skalieren. Wenn Verbraucher dies pro benutzerdefiniertem Element tun möchten, können sie dies dennoch tun, da es sich nur um eine Funktion handelt. Dies ermöglicht es React, eine Meinung zu haben, zu entkommen und alle Anwendungsfälle zu erfüllen, indem die Verantwortung für alle Randfälle an den Verbraucher abgegeben wird. Dies habe ich auch zuvor mit @developit über die Implementierung in Preact besprochen. Die Abstimmung zwischen Preact / React wäre hier ein großer Gewinn.

Die Sorge um zukünftige HTML-Attribute ist etwas, das wir nicht lösen können, daher denke ich, dass wir uns hier nicht damit beschäftigen sollten.

Diese ReactDOM.defineCustomElementProp() Aufrufe könnten in einer JS-Datei bereitgestellt werden, die vom Autor des benutzerdefinierten Elements verwaltet wird

Dies würde die Implementierung des benutzerdefinierten Elements an die bibliotheksspezifische Implementierung (in diesem Fall React) binden. Meiner Meinung nach ist das eine zu hohe Belastung für die Autoren von benutzerdefinierten Elementen. Darüber hinaus führt die Überzeugung von Autoren, die React nicht verwenden, dazu, die Definition zu versenden, in politische Diskussionen, die niemand will.

Das Definieren des Aufrufs einer solchen API durch den Benutzer eines benutzerdefinierten Elements nur mit den Eigenschaften/Attributen, die der Benutzer tatsächlich verwendet, ist weniger Code zu warten und ausdrucksvoller.

Selbst wenn ein Bibliotheksautor React nicht verwendet, argumentiere ich, dass es für den Client dieser Webkomponenten eine bessere UX ist, die Eigenschaftskonfiguration einmal zu definieren und dann zu verwenden.

Das erste Mal, wenn Sie eine Webkomponente verwenden, die eine Eigenschaft benötigt, ist es genauso schwierig wie das Hinzufügen eines Siegels (ausführlicher, aber auch expliziter), aber dann ist es für spätere Verwendungen einfacher, da Sie nicht darüber nachdenken müssen jede einzelne Callsite dieser Komponente. In beiden Fällen müssen Sie die Unterschiede zwischen den Eigenschaften/Attributen verstehen, wenn Sie eine neue Webkomponente übernehmen, aber mit der einzelnen freigegebenen Konfiguration müssen Sie nicht jeden Benutzer dieser Komponente in derselben App darüber nachdenken.

Wir könnten möglicherweise die Neuzuordnung von Eigenschaftsnamen in dieser Konfiguration zulassen, um dort mehr React-idiomatische Namen zu erstellen. Der Upgrade-Pfad zu einem komplexeren Wrapper, falls einer jemals benötigt wurde, ist ebenfalls reibungsloser, da Sie XFoo einfach durch eine benutzerdefinierte React-Komponente ersetzen können, die jede beliebige ausgefallene Übersetzungslogik ausführt, die sie benötigt.

Grundsätzlich: Wenn es möglich ist, dass die Leute nicht jedes Mal über die Eigenschaftskonfiguration nachdenken, wenn sie eine Komponente verwenden, wäre mir dieser Ansatz lieber. Aus diesem Grund habe ich Option 5 vorgeschlagen. Ich denke, sie ist ergonomischer als 3 und 4, während sie genauso flexibel ist.

Option 1 und 2 funktionieren beim Server-Rendering (HTML-Generierung) nicht besonders gut, und Option 2 schafft auch Upgrade-Gefahren für Autoren von Webkomponenten, bei denen das Hinzufügen einer Eigenschaft mit demselben Namen wie ein vorhandenes Attribut jetzt eine bahnbrechende Änderung ist. Wenn wir entscheiden, dass SSR nicht nützlich ist, ist Option 1 aus React-Perspektive ziemlich ansprechend. Ich weiß jedoch nicht, ob es in der Praxis umfassend genug wäre – stellen Komponentenautoren alles über Eigenschaften zur Verfügung?

@treshugart, ohne zu tief in den Fahrradabwurf einzusteigen, denkst du, du könntest zeigen, wie die "Fluchtluke" funktionieren würde? Pseudocode ist in Ordnung. Nur damit wir alle auf der gleichen Seite sind.

Sorry @sophiebits sieht deine Antwort erst jetzt. Ich würde gerne auf einige der Punkte eingehen, nachdem ich sie noch ein paar Mal durchgelesen habe, wollte aber schnell auf diesen antworten:

Ich weiß jedoch nicht, ob es in der Praxis umfassend genug wäre – stellen Komponentenautoren alles über Eigenschaften zur Verfügung?

Jede mit Polymer oder Skate gebaute Komponente legt alles über Eigenschaften offen. Es mag ein paar seltene Ausreißer geben (vielleicht ein Attribut nur für das Styling oder so einstellen), aber ansonsten denke ich, dass die Dinge immer durch Eigenschaften unterstützt werden. Ich bin mir ziemlich sicher, dass die meisten in der Produktion befindlichen Webkomponenten mit einer dieser beiden Bibliotheken erstellt werden.

Wenn jemand eine Vanilla-Webkomponente von Hand erstellt, zwingt ihn nichts, alles über Eigenschaften verfügbar zu machen, aber wir empfehlen dies in unseren Best Practices-Dokumenten und -Beispielen .

Es gibt auch nichts, was sie dazu zwingt, Attribute zu verwenden. Eine benutzerdefinierte Elementdefinition ist nur eine Klasse, sodass der Entwickler tun und lassen kann, was er will. Aber in der Praxis ziehen es die meisten Leute vor, eine Abstraktion wie Polymer oder Skate zu verwenden, um ihre Komponenten zu prägen, so dass sie eine Eigenschaften-API kostenlos erhalten.

Vielleicht ist es dann der beste Ansatz, Eigenschaften immer auf der Clientseite zu erstellen und dann eine Konfigurationszuordnung im Stil von Option 5 von primitiven Eigenschaftsnamen zu Attributnamen für Personen zu unterstützen, die SSR in ihren Apps unterstützen möchten.

@sophiebits : Ich denke, das ist eine großartige Idee, außer insofern, als es eine bahnbrechende Änderung für Leute ist, die ihre React-Apps mit Attributnamen geschrieben haben. Zum Beispiel:

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

Aber was ist mit einer Regel für "Do-Eigenschaften", wenn der Propname keine Bindestriche enthält, und "Do-Attribute", wenn dies der Fall ist?

@robdodson : Benutzer unterschiedliche Groß- / longname Attribut mit einer longName Eigenschaft? Das scheint tatsächlich das viel häufigere Muster mit eingebauten HTML-Elementen und globalen Attributen zu sein (zB contenteditable => contentEditable ), aber ich bin mir nicht sicher, ob es jetzt ausreichend entmutigt ist für benutzerdefinierte Elementattribute dank Polymer und Skate. Wenn es immer noch ein Problem ist, dann vorhandenes JSX mit:

<x-foo longname={val} />

würde als Eigenschaftssatz fehlschlagen, wenn die Eigenschaft longName .

@effulgentsia Ich kann nur für Skate sprechen, aber wir empfehlen die Verwendung der Attribut-API nur beim Schreiben von HTML, was - im Grunde genommen - auch als Server-Rendering klassifiziert werden kann. Wenn Sie etwas über JS tun, sollten Sie Requisiten festlegen. Unsere props API führt automatisch eine unidirektionale Synchronisierung/Deserialisierung von Attributen durch und leitet den Attributnamen durch Bindestrich des Eigenschaftsnamens ab (camelCase wird zu camel-case usw.). Dieses Verhalten als Ganzes ist konfigurierbar, wir empfehlen jedoch die oben genannte Best Practice.

Wie viele sagten, erschweren Requisiten SSR, und dies ist ein berechtigtes Anliegen. Da es so klingt, als würden die meisten Option 1 bevorzugen, versuchen wir vielleicht, den Vorschlag von @sophiebits voranzutreiben , Requisiten auf dem Client bereitzustellen ? Ich gehe davon aus, dass dies bedeutet, dass Attribute auf dem Server festgelegt werden?

Im Interesse eines Beispiels, hier ist , wie Sie Zustand und Requisiten vom Server an den Client mit Skate tragen über implementieren könnten (und alle benutzerdefinierten Elemente , das implementiert einen renderedCallback() und props und / oder state Setter. Dies auf benutzerdefinierter Elementebene zu tun, ist trivial. Wenn React den Weg gehen würde, Attribute auf dem Server zu setzen, wäre die Rehydration im Wesentlichen dasselbe. Die Autoren von Skate-Komponenten müssten tatsächlich nichts tun wie wir würde bereits die Deserialisierungslogik für sie bereitstellen.

@robdodson Ich würde etwas Ähnliches tun wie inkrementelles DOM. Das würde ungefähr so ​​aussehen:

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

Option 2 führt auch zu Upgrade-Gefahren für Autoren von Webkomponenten, bei denen das Hinzufügen einer Eigenschaft mit demselben Namen wie ein vorhandenes Attribut jetzt eine bahnbrechende Änderung darstellt.

Ich denke, dies kann angesichts der Funktionsweise von Attributen auf der Plattform unvermeidlich sein. Wenn Sie ein benutzerdefiniertes Element per SSR verwenden und daher in ein Attribut schreiben müssen, laufen Sie auch Gefahr, dass die Plattform in Zukunft ein ähnlich benanntes Attribut ausliefert. Wie @treshugart bereits erwähnt hat , glaube ich nicht, dass React (oder wirklich jede Bibliothek) dazu befugt ist, es zu lösen. Dies ist etwas, das ich mit den Autoren von Webkomponentenspezifikationen bei TPAC ansprechen möchte, um zu sehen, ob wir es im benutzerdefinierten Elementbereich beheben können.

Ich bin mir nicht sicher, ob das Ihre Meinung zu Option 2 ändert 😁 aber ich wollte es erwähnen, da Option 2 ein paar nette Boni hat und Preact es in der Praxis zu beweisen scheint.

Vielleicht ist es dann der beste Ansatz, Eigenschaften immer auf der Clientseite zu erstellen und dann eine Konfigurationszuordnung im Stil von Option 5 von primitiven Eigenschaftsnamen zu Attributnamen für Personen zu unterstützen, die SSR in ihren Apps unterstützen möchten.

+1, ich unterstütze diesen Weg weiter.


@efffulgentsia

Ich denke, das ist eine großartige Idee, außer dass es eine bahnbrechende Änderung für Leute ist, die ihre React-Apps mit Attributnamen geschrieben haben.

Option 2 würde das lösen :)
Andernfalls müsste wahrscheinlich eine mit Option 1 gekoppelte Heuristik vorhanden sein, die Attribute mit Bindestrichen auf camelCase-Eigenschaften zuordnet.

Aber was ist mit einer Regel für "Do-Eigenschaften", wenn der Propname keine Bindestriche enthält, und "Do-Attribute", wenn dies der Fall ist?

Es sieht so aus, als würde Preact das Attribut setzen, wenn der Name einen Bindestrich enthält. Ich gehe davon aus, dass dies daran liegt, dass sie Option 2 verwenden und long-name die in Prüfung nicht besteht, sodass sie auf ein Attribut ( source ) zurückfällt.

Ich persönlich mag dieses Verhalten, aber es geht zurück in den Bereich der Einstellung von Attributen und möglicher zukünftiger Konflikte, daher denke ich, dass @sophiebits abwägen sollte.

Sind Ihnen benutzerdefinierte Element-Frameworks oder -Praktiken bekannt, bei denen Benutzer eine andere Groß-/Kleinschreibung von Attributen als die entsprechende Eigenschaft haben würden, ohne einen Bindestrich zu enthalten?

Nicht, dass ich davon Wüste. Der Bindestrich ist eine einfache Möglichkeit für die Bibliothek, zu wissen, wo sie eine Kameltasche haben soll. Wenn Sie eine Vanilla-Webkomponente von Hand erstellen würden, könnten Sie longname/longName tun und nur Option 2 würde Sie retten, weil sie keine longname Eigenschaft finden würde, aber sie würde auf die zuvor verwendete zurückgreifen longname Attribut.

Es sieht so aus, als würde Preact als letztes Mittel toLowerCase() für eine Eigenschaft aufrufen, die es nicht erkennt, bevor das Attribut festgelegt wird. Wenn Sie also <x-foo longName={bar}/> SSR ausführen, würde es auch korrekt auf das Attribut longname="" zurückgreifen.


@treshugart

Vielleicht versuchen wir, den Vorschlag von @sophiebits voranzutreiben , Requisiten auf dem Client bereitzustellen ? Ich gehe davon aus, dass dies bedeutet, dass Attribute auf dem Server festgelegt werden?

ja und ja.

Ich würde etwas Ähnliches machen wie Incremental DOM

Ist die Idee, dass jedes Element (oder seine Basisklasse) dies einmischen muss?

btw, danke euch allen, dass ihr euch weiterhin an der Diskussion beteiligt, ich weiß, es ist ziemlich lang geworden ️

Ich bin mir nicht sicher, ob ich das letzte Beispiel in https://github.com/facebook/react/issues/11347#issuecomment -339858314 verstehe, aber es ist sehr unwahrscheinlich, dass wir einen solchen global überschreibbaren Hook bereitstellen würden.

@gaearon Ich denke, Trey hat die

Was halten Sie nach den jüngsten Kommentaren von diesem Vorschlag?

  1. Ändern Sie für BC nichts an der Funktionsweise von benutzerdefinierten Elementen in Kleinbuchstaben in React. Mit anderen Worten, JSX wie <x-foo ... /> würde in React 17 weiterhin genauso funktionieren wie in 16. Oder, wenn _kleinere_ Bugfixes dafür gewünscht werden, öffnen Sie ein separates Thema, um diese zu diskutieren.

  2. Fügen Sie eine API ohne Konfiguration hinzu, um eine React-Komponente mit besserer benutzerdefinierter Elementsemantik zu erstellen. Beispiel: const XFoo = ReactDOM.customElement('x-foo'); .

  3. Verwenden Sie für die oben erstellte Komponente die folgende Semantik:

  4. Für jede Requisite auf XFoo, die ein reservierter React-Requisitenname ist ( children , ref , irgendwelche anderen?), wenden Sie die übliche React-Semantik darauf an (setzen Sie sie weder als Attribut noch als Eigenschaft fest auf dem DOM-Knoten des benutzerdefinierten Elements).
  5. Für jedes Htmlelement globalen Attribut oder Eigenschaft von der HTML - Spezifikation definiert (einschließlich data-* und aria-* ), das gleiche tut mit diesen Requisiten wie das, was mit ihnen tun reagieren , wenn sie auf einen waren div Element. Mit anderen Worten, die Art und Weise, wie React die Requisiten von <XFoo data-x={} className={} contentEditable={} /> auf den DOM-Knoten anwendet, sollte identisch sein wie für <div data-x={} className={} contentEditable={} /> (sowohl für Client-Seite als auch für SSR).
  6. Geben Sie für jede Requisite auf XFoo, die einen Bindestrich enthält (außer den oben zulässigen globalen Attributen), eine Warnung aus (um den Entwickler zu informieren, dass XFoo eine eigenschaftszentrierte und keine attributzentrierte API ist) und unternehmen Sie nichts damit (weder noch als Eigenschaft oder Attribut festlegen).
  7. Für jede Requisite auf XFoo, die mit einem Unterstrich oder einem oberen ASCII-Buchstaben beginnt, tun Sie nichts damit (und geben Sie vielleicht eine Warnung aus?). Beides ist keine empfohlene Methode zum Benennen von Eigenschaften für benutzerdefinierte Elemente, also reservieren Sie diese Namespaces für zukünftige Semantik (siehe beispielsweise unten Nr. 4).
  8. Legen Sie für alle anderen Requisiten auf XFoo beim clientseitigen Rendern auf dem DOM-Knoten als Eigenschaft fest. Wenn der Wert für SSR primitiv ist, rendern Sie ihn als Attribut; wenn nicht primitiv, überspringen Sie das Rendern und legen Sie es während der Hydratation als clientseitige Eigenschaft fest. Für das SSR-Rendering als Attribut ist camelCaseToDashCase der Name.

  9. Reservieren Sie für Komponenten, die über die API in Nr. 2 oben erstellt wurden, einen Prop-Namen, der für Ereignis-Listener verwendet wird. ZB 'events' oder 'eventListeners' . Oder, wenn Sie nicht mit diesen potenziellen benutzerdefinierten Elementeigenschaftsnamen in Konflikt geraten möchten, dann '_eventListeners' oder 'EventListeners' . Die von ReactDom erstellte Implementierung von XFoo würde diese Ereignis-Listener automatisch auf dem DOM-Knoten registrieren.

  10. Für Randfälle (z. B. die Verwendung eines benutzerdefinierten Elements, für das die obige Implementierung nicht erwünscht oder nicht ausreichend ist) könnte der App-Entwickler seine eigene React-Komponente implementieren, um alle speziellen Dinge zu tun, die er benötigt. Dh, sie müssen ReactDOM.customElement() oder sie könnten es erweitern.

  11. Für Leute, die alle oben genannten Verhaltensweisen wünschen, aber möchten, dass ihr JSX die benutzerdefinierten Elementnamen in Kleinbuchstaben verwendet ( <x-foo /> anstelle von <XFoo /> , aus ähnlichen Gründen bevorzugen Leute, die mit dem Schreiben von HTML vertraut sind, <div> über <Div> ), können sie React.createElement() mit Monkey-Patches patchen. Es wäre ein ziemlich einfacher Affen-Patch: Nehmen Sie einfach das erste Argument und wenn es mit einem benutzerdefinierten Elementnamen übereinstimmt, für den Sie dies wünschen (ob das eine bestimmte Liste oder eine beliebige Zeichenfolge mit nur Kleinbuchstaben und mindestens einem Bindestrich ist), dann rufen Sie ReactDOM.customElement() auf diesen Namen und leiten das Ergebnis und die restlichen Argumente an das ursprüngliche React.createElement() .

@developit @gaearon könnte es auch sein. Wenn ein "Mapping" nötig war, halte ich einen Hook für nachhaltiger. Dies sollte jedoch auch die Nettoveränderung zeigen, wenn sie im Kern von ReactDOM implementiert würde, wie Jason betonte.

Ändern Sie für BC nichts an der Funktionsweise von benutzerdefinierten Elementen in Kleinbuchstaben in React. Mit anderen Worten, JSX wie würde in React 17 weiterhin genauso funktionieren wie in 16. Oder, wenn kleinere Fehlerkorrekturen dafür gewünscht werden, öffnen Sie ein separates Thema, um diese zu diskutieren.

Persönlich würde ich es vorziehen, mein <x-foo> Element so verwenden zu können, wie es ist, und es einfach Eigenschaften übergeben zu können, ohne es zuerst umschließen zu müssen.

Wenn möglich, würde ich lieber mit @sohpiebits ' Vorschlag von Option 1 (clientseitige Eigenschaften) und 5 (Konfiguration für SSR) gehen. Ich hege immer noch die Hoffnung, dass die Leute vielleicht Option 2 (vielleicht Option 2 + 5?) für den Rückwärtskompatibilitätsbonus überdenken.

@gaearon ändert sich Ihre Meinung zu

Mein Hauptanliegen bei Option 5 ist es, eine Menge Boilerplate zu erstellen, um Interoperabilität zu ermöglichen und den DX zu unterbrechen.

Ich fand es wirklich toll, wie das React-Team mit den letzten Änderungen im Repo umgegangen ist, wie _PropTypes_ Elemente.

Ich denke, dass die Lösung, die uns alle zufrieden stellen wird, einige dieser Optionen wie _Schritte_, API-Hinzufügen, Warnung und Einstellung oder Verhaltensänderung kombiniert.

Vielleicht Optionen 5 mit Warnung, später Option 2 mit veraltetem Wrapper.

Vielleicht Optionen 5 mit Warnung, später Option 2 mit veraltetem Wrapper.

Ich dachte eigentlich, dass es eine bessere Reihe von Schritten wäre, das Gegenteil zu tun. Option 1 oder 2, weil es weniger dramatisch ist. Und messen, wie das geht und wie die SSR-Geschichte für Webkomponenten aussieht. Dann folgen Sie Option 5, da sie eine neue API und eine Vorstellung von Proxy-/Wrapper-Komponenten hinzufügt.

Das Hauptproblem bei Option 1 ist, dass es sich um einen ziemlich großen BC-Bruch handelt. Das Hauptproblem bei Option 2 besteht darin, dass es eine Racebedingung gibt, die davon abhängt, wann das Element aktualisiert wird. Ich bin mir nicht sicher, ob beides für ein Projekt, das so weit verbreitet ist wie React, wirklich akzeptabel ist.

Angesichts der Verfügbarkeit von Option 5 frage ich mich, ob wir stattdessen viel sicherere, aber immer noch nützliche Verbesserungen an Verwendungen ohne Option 5 vornehmen können. Wie wäre es zum Beispiel mit der Umkehrung von Option 4: Führe einfach domProperties und eventListeners Requisiten ein, damit du Dinge tun kannst wie:

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

Dies ist vollständig abwärtskompatibel, da domProperties und eventListeners aufgrund ihrer Großbuchstaben keine gültigen Attributnamen sind . Abgesehen davon ruft React 16 derzeit setAttribute() sogar für Requisitennamen mit oberen Alphas auf und verlässt sich darauf, dass die Browser den Namen intern klein schreiben; Könnte jedoch eine zukünftige Nebenversion von React 16 eine Warnung ausgeben, wenn benutzerdefinierte Elementeigenschaften gefunden werden, die keine gültigen Attributnamen sind, damit Benutzer ihre Groß-/Kleinschreibungsfehler vor dem Upgrade auf React 17 beheben können?

Dieser Ansatz behält nicht nur BC bei, sondern ist auch leicht zu verstehen: Er ist attributzentriert (was bedeutet, dass React-Requisiten als Elementattribute behandelt werden), was passt, da der Elementname selbst nur in Kleinbuchstaben geschrieben ist und einen Bindestrich hat und daher HTML-zentriert ist. domProperties wird jedoch für die relativ seltenen Fälle bereitgestellt, in denen Sie Eigenschaften übergeben müssen, z. B. um umfangreiche Daten zu übergeben.

Für Leute, die ihr mentales Modell so ändern möchten, dass es eigenschaftszentriert ist (wie Option 1), könnte hier eine API im Stil von Option 5 ins Spiel kommen:

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

Bei dieser Syntax wird jede Requisite als Eigenschaft behandelt (Option 1). Das passt, denn auch das Element "name" (XFoo) folgt einer JS-zentrierten Konvention. Ich denke, wir möchten immer noch mindestens data-* und aria-* Requisiten unterstützen, die als Attribute behandelt werden Attribut.

In der Zwischenzeit könnten wir zur Unterstützung der Konfiguration von SSR Option 5 eine Konfigurations-API zu ReactDOM.customElement hinzufügen, wie zum Beispiel:

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

Vielleicht könnte ssrConfiguration eine Rückruffunktion ähnlich der ReactDOM.setAttribute in @treshugarts Kommentar sein ?

Was denken Sie?

@effulgentsia Ich mag, wohin deine Gedanken gehen. Bikeshedding die Namen ein wenig: domProps / domEvents . Dies ist sehr nahe an Option 4.

WRT SSR, ich denke, SSR kann von den benutzerdefinierten Elementen selbst gehandhabt werden, solange React Attribute respektieren kann, die die Komponente auf sich selbst mutiert, wenn sie sie nicht verfolgt. Ich habe vorhin ein Wesentliches gepostet, aber hier ist es der Einfachheit halber noch einmal: https://gist.github.com/treshugart/6eff0da3c0bea886bb56589f743b78a6. Im Wesentlichen wendet die Komponente Attribute nach dem Rendern auf dem Server an und rehydriert sie auf dem Client. SSR für Webkomponenten ist möglich, aber Lösungen werden noch auf Standardebene diskutiert, daher ist es möglicherweise am besten, auf diesen Teil des Vorschlags zu warten.

@effulgentsia Ich mag auch, wohin du gehst. @sophiebits , @gaearon do
Habt ihr alle Gedanken zu dieser Richtung?

Am Dienstag, 31. Oktober 2017, 19:33 Uhr schrieb Trey Shugart [email protected] :

@effulgentsia https://github.com/effulgentsia Ich mag es, wo du bist
Gedanken gehen. Bikeshedding die Namen ein wenig, es könnte nützlich sein,
ihre Benennung ausrichten: domProps / domEvents oder so.

WRT-Option 2, es ist zumindest abwärtskompatibel und löst die meisten Anwendungsfälle,
aber ich komme zu den Alternativen.

Ich denke, SSR kann von den benutzerdefinierten Elementen selbst verarbeitet werden, solange
React könnte Attribute respektieren, die die Komponente auf sich selbst mutiert, wenn sie
sie nicht verfolgen. Ich habe vorhin ein Wesentliches gepostet, aber hier ist es wieder, denn
Bequemlichkeit:
https://gist.github.com/treshugart/6eff0da3c0bea886bb56589f743b78a6.
Im Wesentlichen wendet die Komponente Attribute nach dem Rendern auf dem Server an
und rehydriert sie auf dem Client. SSR für Webkomponenten ist möglich, aber
Lösungen werden noch auf Standardebene diskutiert, also kann es sein
Warten Sie am besten mit diesem Teil des Vorschlags hier ab.


Sie erhalten dies, weil Sie erwähnt wurden.
Antworten Sie direkt auf diese E-Mail und zeigen Sie sie auf GitHub an
https://github.com/facebook/react/issues/11347#issuecomment-340960798 ,
oder den Thread stumm schalten
https://github.com/notifications/unsubscribe-auth/ABBFDeiQhBWNGXNplbVV1zluYxT-ntFvks5sx9hngaJpZM4QD3Zn
.

Bikeshedding die Namen ein wenig: domProps / domEvents.

Ich mag diese. Ich überlege auch, ob es eine Möglichkeit gibt, sie noch idiomatischer zu React zu machen, indem das Konzept von "dom" durch das Konzept von "ref" ersetzt wird. Mit anderen Worten: refProps / refEvents , da es darum geht, Requisiten und Ereignis-Listener an die "ref" anzuhängen.

Und dann dachte ich, was wäre, wenn wir, anstatt neue spezielle Namen in this.props einzuführen, einfach das vorhandene ref JSX-Attribut überladen würden. Derzeit ist "ref" eine Funktion, die aufgerufen wird, wenn die React-Komponente ein- und ausgehängt wird. Was wäre, wenn wir zulassen, dass es auch so ein Objekt ist:

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

Nur eine Idee.

Dieser Ansatz gefällt mir sehr gut :)

Freundlicher Ping darauf. @gaearon @sophiebits habt ihr alle eine Meinung zu

Wir haben gerade einen RFC-Prozess geöffnet. Könnten wir einen von Ihnen bitten, ihn als RFC einzureichen?
https://github.com/reactjs/rfcs

Ich würde domProperties und eventListeners "props" (oder das Äquivalent innerhalb eines ref={{}}-Objekts) lieber nicht hinzufügen, da dies die Verwendung benutzerdefinierter Elemente sehr unnatürlich und im Gegensatz zu allen anderen React-Komponenten macht. Wenn der Komponentenbenutzer den Unterschied zwischen Eigenschaften und Attributen usw. genau kennen muss, würde ich dies lieber als Lösung im Stil von ReactDOM.createCustomElementType tun. Wenn Sie die Komponente dann genau einmal verwenden, ist es ein vergleichbarer Arbeitsaufwand (Konfiguration angeben und dann einmal verwenden), aber wenn Sie die Komponente viele Male verwenden, müssen Sie nicht jedes Mal über das Konfigurationsobjekt nachdenken. Die Anforderung, dass die Konfiguration jedes Mal angegeben wird, scheint die Ziele einer sauberen Integration benutzerdefinierter Elemente zu verfehlen, es sei denn, ich vermisse etwas.

@sophiebits OK, ich könnte versuchen, einen RFC für so etwas zu schreiben. Was halten Sie von der Idee, die Sie am 26. Oktober auf den Weg gebracht haben, zuerst Eigenschaften auf der Clientseite zu verwenden und den Leuten auch zu erlauben, ReactDOM.createCustomElementType für SSR zu schreiben und / oder wenn sie eine wirklich feine Kontrolle darüber wünschen, wie der Client Attribute/Eigenschaften zuordnet? ?

Zumindest mit dem createCustomElementType Stil können Bibliotheken ihre APIs problemlos darauf abbilden. Dh unser skatejs/renderer-react könnte einfach props und das benutzerdefinierte Element für React konfigurieren. Diese Art von Vanille lässt die Leute jedoch hoch und trocken zurück, ohne eine Abstraktion oder ein bisschen Arbeit zu leisten. Ich mag Robs Vorschlag eines sicheren Standardwerts, der gleichzeitig eine feinkörnige Kontrolle ermöglicht. Wäre das etwas, das funktionieren würde?

@robdodson Ich glaube, ich bin dabei. Ich kann nicht versprechen, dass keine weiteren Bedenken auftauchen werden, aber ich denke, es fühlt sich wie eine gute Balance an.

Option 3 ist bisher die beste Option, und sie kann mit SSR funktionieren. Ich werde eine Idee erklären, wie.

Bisher alle Optionen für sich, außer 3,

  • entweder einen hohen Wartungsaufwand für alle außerhalb der React-Quelle verursachen
  • oder sie zwingen alle Autoren von benutzerdefinierten Elementen im gesamten Universum, sich an React-spezifische Regeln zu halten.

Hier sind universelle Regeln, über die wir uns alle einig sind:

  • Das Festlegen von Attributen mit setAttribute ist die Standardmethode im Universum, um Daten so an Elemente zu übergeben, dass sie 1-zu-1 mit deklarativen HTML-Attributen übereinstimmen. Dies muss als Gesetz des Universums zu 100 % der Zeit funktionieren, daher ist es der einzige zu 100 % garantierte Weg, Daten an Elemente zu übergeben.
  • einige Leute sind nicht glücklich, dass es nur für Saiten entwickelt wurde.
  • Einige Elemente (ich wiederhole, _nur einige Elemente_ ) akzeptieren Werte über Objekteigenschaften, die bestimmten Attributen zugeordnet sind. Darauf kann man sich _nicht_ zu 100% verlassen.
  • manche Leute mögen Objekteigenschaften, weil sie Nicht-Strings akzeptieren können

Wenn React also zu 100% mit _jedem einzelnen benutzerdefinierten Element im Universum arbeiten und keine Last für die Menschen darstellen möchte_ , dann:

  • React muss erkennen, dass es nicht Gott ist, es gibt viele andere Bibliotheken, die die Leute neben React benutzen,
  • Daher sollte React _standardmäßig_ Daten einfach über setAttribute da dies zu 100% Standard ist.
  • React sollte die Tatsache akzeptieren, dass Autoren von benutzerdefinierten Elementen die setAttribute Methoden in ihren Klassendefinitionen erweitern/überschreiben können, sodass setAttribute andere Dinge als Zeichenfolgen akzeptiert. Ein Paradebeispiel können Bibliotheken wie A-Frame sein.
  • React sollte akzeptieren, dass, wenn ein Autor eines benutzerdefinierten Elements möchte, dass ein benutzerdefiniertes Element mit jeder möglichen Bibliothek funktioniert , setAttribute verlässt , um sein Element standardmäßig mit allem kompatibel zu machen, und wenn standardmäßig alle Bibliotheken auf Attribute angewiesen sind, dann funktioniert das gesamte Universum miteinander. _Da gibt es kein Wenn und Aber!_ (es sei denn, das w3c/whatwg nimmt einige große Änderungen vor)

Soooooooo , das sollten wir zumindest tun

Dann können wir über die Auswirkungen von Elementen nachdenken, die manchmal eine Objekteigenschafts-API haben:

  • Entwickler haben die Wahl, setAttribute da sie wissen, dass es die ganze Zeit funktioniert.
  • Entwickler können manchmal Objekteigenschaften nutzen.
  • Entwickler müssen sich immer bewusst sein, ob für ein bestimmtes Element (benutzerdefiniert oder nicht) eine Objekt-Eigenschafts-Schnittstelle existiert oder nicht.
  • Objekt-Eigenschafts-Schnittstellen sind eine alternative Schnittstelle, die nicht unbedingt 1-zu-1 mit Attributen abbildet.

Mit diesem Wissen über Attribute vs. Eigenschaften sollte eine Lösung in React, die _den 100%-Standard erweitern und die Gesetze des Universums respektieren möchte_ :

  1. zulassen, dass Attribute standardmäßig zu 100% funktionieren. Das bedeutet, dass <x-foo blah="blah" /> _standardmäßig_ auf setAttribute abgebildet und den Wert an "[object Object]" Zeichenfolge übergeben wird.
  2. Überlegen Sie sich eine alternative Möglichkeit, den React-Benutzer

Es scheint, als ob Option 3 mit einem Siegel (deren zusätzliche Syntax ehrlich gesagt nicht schwer zu erlernen ist) eine Lösung ist, die dem Ideal am nächsten kommt. Basierend auf dieser SO-Frage ist das einzige verfügbare Symbol = , obwohl es besser lesbar ist, sich auf etwas wie & festzulegen (mit einer Escape-Form, vielleicht wie \& ). Zum Beispiel, wenn ich speziell eine Requisite haben möchte:

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

Die meisten anderen Zeichen, die von der WHATWG-HTML-Syntaxspezifikation abgedeckt werden, sollten funktionieren, und ich hoffe, dass sie es tun, aber das ist ein anderes Thema.

Option 3 ist bisher die beste Option.

Wenn beim Client keine Flüssigkeitszufuhr verwendet wird und daher Requisiten in SSR keinen Sinn machen, dann na ja. Es hat noch nie funktioniert und es muss auch jetzt nicht funktionieren. SSR im PHP- oder Java-Stil sendet statisches HTML ohne Hydratation und sie verlassen sich zu 100% auf Attribute. Es heißt, wenn wir React SSR verwenden, werden wir wahrscheinlich clientseitige Flüssigkeitszufuhr verwenden, und wenn wir keine Flüssigkeitszufuhr wünschen, sollten wir uns einfach der Tatsache bewusst sein, dass wir in diesem Fall keine Requisiten verwenden sollten. _Das ist einfach. Alles, was Sie tun müssen, ist, diesen Vorbehalt in der Dokumentation deutlich zu machen._

Aber!!! Das ist nicht alles! Wir können immer noch die Funktionen von Option 5 einschließen, um den Benutzern mehr Kontrolle zu geben. Mit einer API wie Option 5,

  • Manche Leute können konfigurieren, wie einige Attribute Requisiten zugeordnet werden können
  • UND wie einige Requisiten Attributen zugeordnet werden können. Dies kann dazu beitragen, dass SSR auch bei der nicht hydratisierenden Art von SSR funktioniert!
  • Wir können die Leute definieren lassen "wenn dieses Attribut geschrieben ist, übergeben Sie es tatsächlich an diese Stütze, aber wenn es SSR ist, übergeben Sie es nicht an die Stütze" oder "übergeben Sie diese Stütze nur während SSR an dieses Attribut, sonst tun Sie was Ich habe mit dem Siegel angegeben", usw

Am Ende scheint folgendes eine Lösung zu sein, die funktionieren würde:

  • Option 3 mit setAttribute die standardmäßig hinter den Kulissen verwendet wird,
  • mit einem Fix für #10070, damit React den Leuten nicht in die Quere kommt,
  • und eine API wie in Option 5 zur weiteren Optimierung für diejenigen, die sie wirklich benötigen, einschließlich Möglichkeiten zur Optimierung von SSR, Client oder beidem.

Frohes neues Jahr!

Ich möchte auf einige der oben genannten Punkte eingehen, befürchte aber, dass dieser Thread bereits _unglaublich_ lang ist. Entschuldigung, dass ich es länger gemacht habe :P

Hier sind universelle Regeln, über die wir uns alle einig sind:

Das Festlegen von Attributen mit setAttribute ist die Standardmethode im Universum zum Übergeben von Daten an Elemente in einer Weise, die 1-zu-1 mit deklarativen HTML-Attributen übereinstimmt. Dies muss als Gesetz des Universums zu 100 % der Zeit funktionieren, daher ist es der einzige zu 100 % garantierte Weg, Daten an Elemente zu übergeben.

Das stimmt nicht ganz. Es gibt nichts in der Plattform, um zu erzwingen, dass ein benutzerdefiniertes Element eine Attributschnittstelle verfügbar macht. Sie können genauso einfach eine erstellen, die nur JS-Eigenschaften akzeptiert. Es ist also kein "garantierter Weg zur Weitergabe von Daten". Das Fehlen von Durchsetzungsmechanismen bedeutet, dass Sie sich auf keinen der beiden Stile (HTML-Attribute oder JS-Eigenschaften) mit 100%iger Sicherheit verlassen können.

einige Leute sind nicht glücklich, dass es nur für Saiten entwickelt wurde.
Einige Elemente (ich wiederhole, nur einige Elemente) akzeptieren Werte über Objekteigenschaften, die bestimmten Attributen zugeordnet sind. Darauf kann man sich nicht zu 100 % verlassen.

Wir empfehlen den Leuten, sich nicht einmal die Mühe zu machen, Attribute für Eigenschaften zu erstellen, die umfangreiche Daten wie Objekte/Arrays akzeptieren. Dies liegt daran, dass einige Daten nicht in eine Attributzeichenfolge zurück serialisiert werden können. Wenn beispielsweise eine Ihrer Objekteigenschaften ein Verweis auf einen DOM-Knoten ist, kann dieser nicht tatsächlich stringifiziert werden. Wenn Sie ein Objekt stringifizieren und erneut analysieren, verliert es seine Identität. Das heißt, wenn es eine Referenz auf ein anderes POJO hat, können Sie diese Referenz nicht wirklich verwenden, da Sie ein völlig neues Objekt erstellt haben.

manche Leute mögen Objekteigenschaften, weil sie Nicht-Strings akzeptieren können

Daher sollte React standardmäßig nur Daten über setAttribute übergeben, da dies zu 100% Standard ist.

JavaScript-Eigenschaften sind ebenso Standard. Die meisten HTML-Elemente stellen sowohl eine Attribute- als auch eine entsprechende Eigenschaftenschnittstelle bereit. Beispiel: <img src=""> oder HTMLImageElement.src .

React sollte die Tatsache akzeptieren, dass Autoren von benutzerdefinierten Elementen die setAttribute-Methoden in ihren Klassendefinitionen erweitern/überschreiben können, sodass setAttribute andere Dinge als Zeichenfolgen akzeptiert.

Autoren könnten das tun, aber das scheint tatsächlich viel "nicht standardmäßig" zu sein, als nur JS-Eigenschaften zu verwenden. Es kann Sie auch seltsamen Problemen im Zusammenhang mit dem Parsen und Klonen des Elements aussetzen.

React sollte akzeptieren, dass, wenn ein Autor eines benutzerdefinierten Elements möchte, dass ein benutzerdefiniertes Element mit jeder möglichen Bibliothek funktioniert, nicht nur mit React, dieser Autor sich auf setAttribute verlässt, um sein Element standardmäßig mit allem kompatibel zu machen, und wenn standardmäßig alle Bibliotheken auf Attribute angewiesen sind , dann wird das ganze Universum miteinander arbeiten. Es gibt kein wenn und und aber dafür! (es sei denn, das w3c/whatwg nimmt einige große Änderungen vor)

Ich bin mir nicht sicher, wie Sie zu dieser Schlussfolgerung gelangen, da es andere Bibliotheken gibt, die es vorziehen, JS-Eigenschaften für benutzerdefinierte Elemente festzulegen (z. B. Angular). Für maximale Kompatibilität sollten Autoren ihre Attribute mit JS-Eigenschaften unterstützen. Damit wird die größte Fläche möglicher Nutzungen abgedeckt. Alle mit Polymer erstellten Elemente tun dies standardmäßig.

zulassen, dass Attribute standardmäßig zu 100% funktionieren. Dies bedeutet, dass sollte standardmäßig setAttribute zugeordnet werden und den Wert unverändert übergeben. Dies ist eine nicht brechende Änderung. Tatsächlich handelt es sich um eine Korrekturänderung, die ansonsten dazu führen würde, dass eine bedeutungslose "[Objektobjekt]"-Zeichenfolge übergeben wird.

Ich denke, React übergibt den Wert tatsächlich unverändert. Der Aufruf von setAttribute('foo', {some: object}) ergibt [object Object] . Es sei denn, Sie schlagen vor, JSON.stringify() für das Objekt aufzurufen? Aber dann ist dieses Objekt nicht "unverändert". Ich denke, Sie verlassen sich vielleicht darauf, dass der Autor setAttribute() überschrieben hat? Es könnte plausibler sein, sie dazu zu ermutigen, entsprechende JS-Eigenschaften zu erstellen, anstatt das DOM mit Affen zu patchen.

Ich denke, React übergibt den Wert tatsächlich unverändert. Der Aufruf von setAttribute('foo', {some: object}) führt zu [object Object]

React wandelt Werte in einen String um, bevor es an setAttribute :

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

und

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

Ich stimme grundsätzlich allem zu, was Sie gesagt haben.

Wir sind uns einig, dass die Leute Dinge in beide Richtungen machen und es keinen Standard gibt, der jeden dazu zwingt, es auf die eine oder andere Weise zu tun, also denke ich immer noch

  • Option 3 mit standardmäßig verwendetem setAttribute und einem Siegel zum Angeben der Verwendung von Instanzeigenschaften,
  • mit einem Fix für #10070, damit React keine Argumente zu Strings zwingt,
  • und Option 5, eine API für die Feinabstimmung

Wenn Option 5 gut gemacht ist, kann die Hydratation für SSR-Lösungen Daten entweder Attributen oder Requisiten zuordnen, wie durch die Verwendung der Option 5 API angegeben.

React erzwingt Werte in einen String, bevor er an setAttribute übergeben wird

Ich verstehe. Da die meisten Leute kein benutzerdefiniertes toString() es standardmäßig [object Object] .

Da die meisten Leute kein benutzerdefiniertes toString() es standardmäßig [object Object] .

Genau wie wenn ich es tue

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

Das Ergebnis ist

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

Als Webentwickler sollten wir uns natürlich bewusst sein, was passiert, wenn wir Nicht-Strings an Elementattribute übergeben. Zum Beispiel bin ich mir bewusst, dass ich Nicht-Strings an A-Frame-Elemente übergeben kann, und ich sollte dies tun können, ohne dass eine Bibliothek im Weg ist.

React muss erkennen, dass es nicht Gott ist, es gibt noch viele andere Bibliotheken, die die Leute neben React benutzen

Das ist unnötig bissig. Sie werden in diesem Thread bemerken, dass uns dies wichtig ist, aber dass es viele verschiedene Optionen und Visionen gibt, wohin das benutzerdefinierte Elementdesign gehen soll. Es ist sicherlich nicht klar, was zu tun ist.

@sebmarkbage Tut

Was ich meinte, ist, dass React sehr beliebt ist, also hat React das Potenzial, Leute dazu zu bringen, Dinge auf eine bestimmte Weise zu tun, die an anderen Orten möglicherweise nicht funktionieren (z. funktioniert nicht mit allen benutzerdefinierten Elementen).

React wandelt derzeit alle an Elementattribute übergebenen Werte in Strings um. Hätte React dies zum Beispiel nicht getan, müsste aframe-react (das das String-Problem

Wenn React uns einfach die Möglichkeit geben kann, wie wir Daten an Elemente übergeben, wie in einfachem JavaScript, wäre ich der zufriedenste Benutzer. 😊

Nochmals Entschuldigung für meine Wortwahl dort. Ich überlege es mir das nächste Mal doppelt.

Ich habe dazu einen Kommentar zum RFC PR hinzugefügt. Ich denke, es ist eine Diskussion wert, da es sowohl die vorgeschlagenen Elemente als auch ein einfacheres Modell zum zuverlässigen Ableiten eines benutzerdefinierten Elements und seiner Eigenschaften abdeckt. Es verwandelt es wieder in einen hybriden Ansatz, bietet aber für die meisten Anwendungsfälle eine Zero-Config-Integration.

Ich bin sehr daran interessiert, diese Funktion in React implementiert zu sehen. Vielen Dank für Ihre Bemühungen, React großartig zu machen.

Irgendein Update zu diesem RFC?

Benutzerdefinierte Elementbibliotheken werden wirklich gut und ich würde sie gerne in meiner React-App verwenden. Gibt es dazu schon Neuigkeiten? Das Umschließen von benutzerdefinierten Elementen und das Stringieren ihres Inhalts, um sie später erneut zu parsen, ist eine ziemlich unpraktische Lösung, wenn man bedenkt, dass Vue und Angular Komponenten problemlos nativ handhaben

Irgendein Update zu diesem Thema?

Auch ich würde gerne benutzerdefinierte Elementbibliotheken verwenden, ohne auf Hacks zurückzugreifen. Ich würde mich freuen, wenn dieses Problem gelöst wird.

@mgolub2 Das React-Team kümmert sich nicht darum, was die Web-Community will. Webkomponenten sind jetzt ein weit verbreiteter Standard, und es gibt keine Signale vom Team, um diesen Standard nach 2 Jahren zu unterstützen.

Hallo zusammen, ich habe eine Pull-Anfrage gestartet, um dieses Problem zu beheben, indem ich zwei Zeilen geändert habe :

Auf diese Weise kann ein Autor eines benutzerdefinierten Elements Folgendes tun:

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

Es gibt viele Variationen, wie eine erweiterte setAttribute Methode aussehen könnte, das ist nur ein kleines Beispiel.

React-Team können Sie argumentieren, dass Autoren von benutzerdefinierten Elementen dies nicht tun sollten, da sie in einigen Fällen die native Attributbehandlung des DOM umgehen (z. B. wenn Werte keine Strings sind). Wenn Sie dieser Meinung sind, bedeutet dies immer noch nicht, dass Sie daran hindern sollten, was Autoren von benutzerdefinierten Elementen mit ihren __ benutzerdefinierten __ Elementen tun können.

React sollte keine Meinungen darüber abgeben, wie vorhandene DOM-APIs verwendet werden. React ist ein Werkzeug zur Manipulation von DOM und sollte nicht davon überzeugt sein, was wir mit dem DOM machen können, sondern nur, wie Daten in das DOM fließen. Mit "Datenfluss zum DOM" meine ich den Weg, den die Daten nehmen, um dorthin zu gelangen, ohne die Daten zu mutieren (das Konvertieren der Objekte eines Autors in Zeichenfolgen mutiert die Daten des Autors).

Warum möchten Sie die Daten des Autors mutieren? Warum können Sie nicht einfach davon ausgehen, dass die Person, die das DOM manipulieren möchte, weiß, welche Daten an das DOM weitergegeben werden sollen?

@trusktr Ich glaube, das wurde in https://github.com/facebook/react/issues/10070 diskutiert

Ich würde die Leute wirklich davor warnen, den Autoren von benutzerdefinierten Elementen zu sagen, dass sie eine integrierte Methode wie setAttribute überschreiben sollen. Ich glaube nicht, dass es für uns gedacht ist, es zu flicken. cc @gaearon

Es ist harmlos, setAttribute in solchen Unterklassen zu überschreiben (das ist kein Affen-Patching). Aber wie gesagt, warum muss React das diktieren, wenn dies nicht unbedingt die Aufgabe der React-Bibliothek ist. Wir wollen React verwenden, um DOM (ohne Behinderung) zu manipulieren.

Wenn ich el.setAttribute() manuell für eine Leistungssteigerung verwenden muss, verschlechtert das auch die Entwicklererfahrung.

Ich glaube nicht, dass das React-Team viele Leute vor einer großen Gefahr rettet, indem es alles, was in setAttribute in Strings umwandelt.

Ich stimme zu, dass eine andere Lösung möglicherweise besser ist. Zum Beispiel die Aktualisierung der JSX-Spezifikation, um eine neue Syntax zu erhalten, aber das scheint lange zu dauern.

Was verliert die Community, wenn wir die automatische String-Konvertierung weglassen? Was verliert das React-Team?

Das React-Team könnte die Situation später mit einer besseren Lösung verbessern...

Warum geben Sie uns nicht zumindest einfach die Möglichkeit, die Stringifizierung zu umgehen? Ist das etwas, das Sie in Betracht ziehen könnten?

Ich würde die Leute wirklich davor warnen, den Autoren von benutzerdefinierten Elementen zu sagen, dass sie eine integrierte Methode wie setAttribute überschreiben sollen.

Können Sie einen guten Grund dafür angeben?

Das scheint ein Ablenkungsmanöver zu sein. "Jeden, der benutzerdefinierte Elemente schreibt, davon überzeugen, eine Methode mit einem bestimmten Verhalten an sein Element anzuhängen" ist keine Lösung für dieses Problem, bei dem es darum geht, wie Eigenschaften für DOM-Elemente festgelegt werden.

@trusktr

Können Sie einen guten Grund dafür angeben?

Webkomponenten sind ein Standard. Daher sollten auch Derivate des Standards standardkonform sein.

Das Überschreiben von setAttribute entspricht jedoch nicht dieser Bedingung: Es erstellt einen Hack nur für React, während es viele andere Frameworks gibt, die mit Webkomponenten sofort funktionieren. Sie müssten also den React-Hack nutzen, auch wenn sie überhaupt nicht mit React arbeiten. Ich glaube nicht, dass es die richtige Lösung ist.

Als nächstes ist bekannt, dass das Patchen der Standardmethoden ein falscher Ansatz ist. Das Überschreiben von setAttribute ändert das ursprüngliche Verhalten, das Endbenutzer verwirren kann, wenn sie versuchen, es zu verwenden. Jeder erwartet, dass Standard als Standard funktioniert, und das benutzerdefinierte Element ist keine Ausnahme, da es das Verhalten von HTMLElement erbt. Und obwohl es mit React funktionieren könnte, schafft es eine Falle für alle anderen Benutzer. Wenn beispielsweise Webkomponenten ohne Framework verwendet werden, kann setAttribute viel aufgerufen werden. Ich bezweifle, dass die Entwickler von benutzerdefinierten Elementen zustimmen würden, mit diesem Ansatz zu schießen.

Persönlich denke ich, dass eine Art React-Wrapper viel vielversprechender aussieht.

Hey Leute, während wir warten, habe ich in der Zwischenzeit ein Shim zum Umhüllen Ihrer Webkomponente in React erstellt 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>
  </>
)

Ich hoffe, das erweist sich als hilfreich

(Dies ist mein erster Ausflug in OSS und einer der ersten Open-Sourcing von etwas außerhalb meines Büros. konstruktive Kritik ist mehr als willkommen )

Hey Leute, während wir warten, habe ich in der Zwischenzeit ein Shim zum Umhüllen Ihrer Webkomponente in React erstellt 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>
  </>
)

Ich hoffe, das erweist sich als hilfreich

(Dies ist mein erster Ausflug in OSS und einer der ersten Open-Sourcing von etwas außerhalb meines Büros. konstruktive Kritik ist mehr als willkommen )

Tolle Hülle :)

Es ist auch erwähnenswert, dass das Erstellen von Webkomponenten mit Stencil dieses Problem mit React behebt: https://stenciljs.com/docs/faq#can -data-be-passed-to-web-components-

@matsgm Ich freue mich, dass das Bauen mit Stencil für Sie funktioniert hat. Aber als Vorsichtsmaßnahme: Unserer Erfahrung nach hat sich Stencil als nicht gut mit anderen Webkomponenten-Frameworks, insbesondere Polymer, erwiesen, und wir haben uns über ein halbes Dutzend anderer Probleme zwischen den Build-Tools, dem Support und der allgemeinen Funktionalität geärgert. Ihr Kilometerstand kann variieren :)

Ich versuche nur, mit diesem Thread auf dem neuesten Stand zu sein. Was ist die endgültige Lösung?

In gewisser Weise bin ich ein großer Fan von expliziter Definition von Attr, Prop, Event und nicht von magischen Heuristiken, die sehr verwirrend sein können.

snabbdom zB verwendet explizite Top-Level-Namensräume, dies macht es einfach zu wissen, was wohin gehört. Es gibt keine String-Manipulation, die schneller ausgeführt werden kann, zB das Trimmen des Suffixes von onClick => click ist immer noch ein Perf-Hit.

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

Ich wünsche, dass JSX Namespaces mit Punktsyntax unterstützt. Dies bedeutet, dass Requisiten der Standard-Namespace wären und wir einfach schreiben könnten

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

Dies ist durch Modifizieren des React JSX Pragma-Parsers erreichbar.

Eine zusätzliche Option besteht darin, propeties (oder props für die reagierenden Fans) als vorgesehenes Wort für die Eigentumsübergabe beizubehalten

Da 17.0 heute veröffentlicht wurde, könnten wir dieses Problem aktualisieren, um den Status widerzuspiegeln, wann das Problem behoben wird?

Ja. Ursprünglich hatten wir React 17 als Release geplant, bei dem viel mehr veraltete Einstellungen entfernt wurden (und mit neuen Funktionen). Es ist jedoch nicht sehr realistisch, dass alte Codebasen ihren gesamten Code aktualisieren, und aus diesem Grund haben wir uns entschieden, React 17 herauszubringen, das sich nur auf eine einzige wesentliche Änderung konzentriert (Ereignisse werden an das Wurzelelement und nicht an das Dokument angehängt .) ). So können alte Apps für immer auf React 17 bleiben und nur Teile davon auf 18+ aktualisieren.

Ich weiß, es ist frustrierend, dass wir gesagt haben, dass dieses spezielle Feature in 17 einfließen würde, aber Sie werden feststellen, dass so ziemlich alle Dinge, die wir ursprünglich für 17 geplant hatten, auch auf 18 verschoben wurden. 17 ist ein spezielles Sprungbrett-Release. Das lässt uns aggressivere Breaking Changes in 18 vornehmen. Eventuell diese einschließen, wenn es einen klaren Weg nach vorne gibt.

Ich bin mir nicht sicher, was der neueste Konsens der WC-Gemeinschaft darüber ist, welche dieser Optionen vorzuziehen ist. Es ist jedoch sehr hilfreich, alle aufgeschrieben zu haben (große Requisiten an @robdodson für diese Arbeit). Ich bin gespannt, ob sich die Meinungen der Leute zu diesen Optionen seit dem Schreiben dieses Threads weiterentwickelt haben und ob es neue Informationen gibt, die uns bei der Orientierung helfen könnten.

Ich glaube nicht, dass die WC-Community ihre bevorzugte Option geändert hat, die immer noch Option 3 ist. @developit kann mehr darüber sagen, wie Preact mit benutzerdefinierten Elementen kompatibel ist, was auch für React interessant sein könnte. Für einen allgemeinen Überblick darüber, wie Frameworks mit der Übergabe (komplexer) Daten an benutzerdefinierte Elemente kompatibel sind, bietet https://custom-elements-everywhere.com/ alle Details.

Beachten Sie, dass Vue in https://github.com/vuejs/vue/issues/7582 ein Siegel verwendet hat und "." als Präfix (nicht Glimmers "@").

In https://github.com/vuejs/vue/issues/7582#issuecomment -362943450 schlug @trusktr vor, dass die korrekteste SSR-Implementierung darin besteht, keine sigillierten Eigenschaften als Attribute im SSR-HTML wiederzugeben und Legen Sie sie stattdessen während der Flüssigkeitszufuhr über JS als Eigenschaften fest.

Ich halte es für ziemlich unwahrscheinlich, dass wir allein wegen dieser speziellen Funktion eine neue JSX-Syntax einführen würden.

Es stellt sich auch die Frage, ob es sich lohnt, Option (3) zu wählen, wenn möglicherweise eine allgemeinere Version der Option (5) auf dem Tisch liegt. Dh Option (5) könnte ein Low-Level-Mechanismus sein, um benutzerdefinierte Low-Level-React-Knoten mit benutzerdefiniertem Mount/Update/Unmount/Hydratation-Verhalten zu deklarieren. Nicht einmal spezifisch für benutzerdefinierte Elemente an sich (obwohl sie einer der Anwendungsfälle wären).

Anstatt eine völlig neue JSX-Syntax für diese spezielle Funktion einzuführen, wie wäre es mit der Einführung einer allgemeineren JSX und deren Verwendung zum Definieren benutzerdefinierter Eigenschaften?

Ich habe vor langer Zeit vorgeschlagen, die Syntax der berechneten Eigenschaften von ECMAScript zu JSX hinzuzufügen (facebook/jsx#108) und denke, dass dies eine nützliche Ergänzung der Syntax im Allgemeinen wäre.

Wenn eine berechnete Eigenschaftssyntax verfügbar wäre, würde dies die Möglichkeit offen lassen, Eigenschaften mithilfe von berechneter Eigenschaftssyntax und Symbolen oder vorangestellten Zeichenfolgen zu definieren.

Zum Beispiel:

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

@dantman Wie geht dieser Vorschlag mit Server-Rendering und -Hydratation um?

Ich glaube nicht, dass irgendjemand in der WC-Community Option 5 will.

Es unterscheidet sich wirklich nicht von der aktuellen Praxis des Patchens, einen benutzerdefinierten React-Wrapper für benutzerdefinierte Elemente zu schreiben. Der massive Nachteil dieses Ansatzes besteht darin, dass er entweder den Komponentenautor für React in Sonderfällen oder den Komponentenverbraucher für benutzerdefinierte Sonderfälle belastet, was beides nicht erforderlich sein sollte.

Was denkst du über Server-Rendering und Hydratation? Wenn keine explizite Konfiguration vorhanden ist, welche Heuristik ist wünschenswert? Gibt es eine Konsolidierung in der Art und Weise, wie dies normalerweise in der WC-Gemeinschaft geschieht? Ich habe dieses RFC noch einmal gelesen und es scheint nicht in Details zu diesem Thema einzutauchen. Aber es ist ziemlich entscheidend, besonders da React mehr Wert auf das Server-Rendering legt (da es oft zu Recht wegen zu Client-zentrierter Standardeinstellungen kritisiert wird).

@gaearon Ich kenne nicht genug

Die allgemeine Idee ist, dass property(propertyName) , je nachdem, wie Sie es implementieren möchten, einen vorangestellten String (zB '__REACT_INTERNAL_PROP__$' + propertyName ) ausgeben oder ein Symbol erstellen und eine "symbol => propertyName"-Assoziation speichern könnte in eine Karte.

Letzteres kann problematisch sein, wenn Sie diese Karte dem Client mitteilen müssen.

Nach meinem groben Verständnis sind Eigenschaften jedoch nicht etwas, das Sie auf dem Server handhaben können, und die Hydratation beinhaltet Client-Code, daher bin ich mir nicht sicher, was der Plan dafür ist. Mein Vorschlag kann wahrscheinlich an alle Pläne angepasst werden, die Sie zur Lösung dieses Problems haben. Wenn Sie planen, mit Eigenschaften auf dem Server zu reagieren, kann er dies einfach tun, wenn er eine der Eigenschaften sieht, die von property .

Als Autor von Webkomponentenbibliotheken ist Option 5 keine bequeme Wahl. Ich möchte benutzerdefinierte Elemente erstellen, ohne für jedes Framework und jede Komponente explizit ein Schema definieren zu müssen. Und viele Autoren von benutzerdefinierten Elementen tun dies einfach nicht, was die Last auf den React-Entwickler drückt.

Niemand gewinnt mit Option 5. 😕

@claviska Haben Sie eine Meinung dazu, wie Server-Rendering und

@gaearon SSR wird in zwei Situationen unterteilt: solche, in denen das benutzerdefinierte Element SSR unterstützt, und solche, in denen dies nicht der Fall ist.

Bei Elementen, die SSR nicht unterstützen, spielt das Festlegen von Eigenschaften auf dem Server keine Rolle. Abgesehen von integrierten reflektierenden Eigenschaften wie id und className können sie gelöscht und nur auf dem Client geschrieben werden.

Für Elemente, die SSR unterstützen, sind Eigenschaften wichtig, aber nur, damit sie jedes Ergebnis auslösen können, das sie auf DOM verursachen. Dies erfordert eine Elementinstanz oder eine Art SSR-Proxy/Stellvertreter und ein Protokoll zum Kommunizieren des SSR-ed DOM-Zustands. Es gibt noch keine gemeinsame serverseitige Schnittstelle für diesen Prozess, daher gibt es hier vorerst wirklich nichts zu tun. Autoren von Webkomponenten und Bibliotheksverwalter müssen einige Dinge herausfinden, bevor React überhaupt Brücken bauen kann.

In die Arbeit meines Teams an SSR haben wir React integriert, indem wir createElement und dangerouslySetInnerHTML . Ich denke, das ist der Grad der Integration/des Experimentierens, auf dem wir uns für eine Weile befinden werden. Ich hoffe, dass wir irgendwann bald auf einige User-Land-Protokolle für Interop innerhalb von SSR-Systemen konvergieren können. Bis dahin ist es absolut sicher und sinnvoll, benutzerdefinierte Elementeigenschaften und Ereignisunterstützung ohne _deep_ SSR zu haben. Das Tag des Elements würde immer noch als untergeordnetes Element einer React-Komponente mit SSR versehen, wie es heute ist.

Abgesehen von integrierten reflektierenden Eigenschaften wie id und className können sie gelöscht und nur auf dem Client geschrieben werden.

Können Sie mir helfen zu verstehen, wie das funktioniert? Angenommen, es gibt <github-icon iconname="smiley" /> . Meinst du, das SSR sollte nur <github-icon /> in die HTML-Antwort einschließen und React setzt dann domNode.iconname = ... während der Flüssigkeitszufuhr? In diesem Fall bin ich mir nicht sicher, ob ich verstehe, was passiert, wenn die benutzerdefinierte Elementimplementierung geladen wird, bevor die React-Hydratation stattfindet. Woher weiß die github-icon Implementierung, welches Symbol gerendert werden soll, wenn iconname nicht im HTML-Code vorhanden ist?

Es gibt noch keine gemeinsame serverseitige Schnittstelle für diesen Prozess, daher gibt es hier vorerst wirklich nichts zu tun. Autoren von Webkomponenten und Bibliotheksverwalter müssen einige Dinge herausfinden, bevor React überhaupt Brücken bauen kann.

Ich bin gespannt, ob etwas Besonderes passieren sollte, damit die Community hier einen Konsens bildet. Ist React derjenige, der es zurückhält? Ich verstehe sehr gut die Frustration, die damit verbunden ist, dass dieses Thema seit 2017 offen ist. Andererseits ist es drei Jahre her, und ich denke, Sie sagen, dass dieser Konsens noch nicht zustande gekommen ist. Was sind die Voraussetzungen dafür? Ist es nur eine Frage des weiteren Experimentierens?

@gaearon Wenn die Implementierung der benutzerdefinierten Elementimplementierung vor der React-Hydratation geladen wird, wird der Standardwert des Attributs iconname zugewiesen. Was meinst du mit if _iconname_ does not exist in the HTML ? Wenn für den HTMLElement-Typ kein Attribut definiert ist, wird es ignoriert. Sobald die benutzerdefinierte Elementdefinition geladen ist, erweitert sie den HTMLElement-Typ und definiert iconname und kann auf einen neuen übergebenen Wert reagieren.

Ich versuche, dies aus der Perspektive eines Benutzers zu verstehen. Wir rendern eine Seite auf dem Server. Es hat ein Symbol in der Mitte eines Textes. Dieses Symbol wird als benutzerdefiniertes Element implementiert. Welche Abfolge von Dingen sollte der Endbenutzer erleben?

Mein Verständnis ist bisher folgendes:

  1. Sie sehen das anfängliche Renderergebnis. Es enthält das gesamte normale Markup, aber das benutzerdefinierte Element <github-icon> wird überhaupt nicht angezeigt. Vermutlich wäre es ein Loch, wie ein leeres Div. Bitte korrigiere mich wenn ich falsch liege (?). Seine Implementierung wurde noch nicht geladen.

  2. Die Implementierung des benutzerdefinierten Elements <github-icon> geladen und registriert. Wenn ich das richtig verstehe, ist dies der Vorgang, der "Upgrade" genannt wird. Obwohl sein JS bereit ist, kann es immer noch nichts anzeigen, da wir iconname in den HTML-Code eingefügt haben. Wir haben also nicht die Informationen, für welches Symbol gerendert werden soll. Dies liegt daran, dass wir zuvor gesagt haben, dass "nicht integrierte Eigenschaften aus dem HTML-Code entfernt werden können". Der Benutzer sieht also immer noch ein "Loch".

  3. Reagieren Sie und der Anwendungscode wird geladen. Hydratation passiert. Während der Hydratation setzt React die Eigenschaft .iconname = gemäß der Heuristik. Der Benutzer kann das Symbol jetzt sehen.

Ist dies eine korrekte Aufschlüsselung für den Fall, dass die JS-Implementierung des benutzerdefinierten Elements zuerst geladen wird? Ist dieses Verhalten für die WC-Gemeinde wünschenswert?

@gaearon Ja, das würde ich in dieser Situation erwarten.

Auf der anderen Seite ist es drei Jahre her, und ich denke, Sie sagen, dass dieser Konsens noch nicht zustande gekommen ist

Ich sage nicht, dass kein Konsens gefunden wurde. Ich sage, Sie müssen sich im Moment keine Sorgen um _deep_ SSR machen und sollten nicht zulassen, dass ziemlich vage Bedenken die grundlegendste clientseitige Interoperabilität blockieren, die erreicht werden kann und im Moment äußerst nützlich ist.

Ich habe diese Unterscheidung noch nie zuvor gesehen (tief vs. flach), also lassen Sie mich versuchen, sie neu zu formulieren, um zu überprüfen, ob ich Sie verstanden habe.

Mit "tiefem" SSR meinen Sie wohl SSR, ähnlich wie es in React-Komponenten funktioniert. Das heißt, das Rendern eines CE würde eine Ausgabe erzeugen, die angezeigt werden kann, bevor die Logik dieses CE geladen wurde. Das ist etwas, was Sie sagen, wir sollten uns jetzt keine Sorgen machen, es zu unterstützen. Das macht für mich Sinn.

Mit "nicht tiefem" SSR meinen Sie, dass wir nur das CE-Tag selbst in HTML einfügen. Ohne sich Gedanken zu machen, worauf es hinausläuft. Das macht für mich auch Sinn. Meine Frage bezog sich jedoch auf _diesen_ Fluss – nicht auf den "tiefen" SSR.

Insbesondere https://github.com/facebook/react/issues/11347#issuecomment -713230572 beschreibt eine Situation, in der wir, selbst wenn wir die CE-Logik _bereits heruntergeladen_ haben, dem Benutzer bis zur Hydratation nichts zeigen können. Wird seine Eigenschaften unbekannt, bis der JS-Client-Code der gesamten Anwendung geladen wird und Hydratation stattfindet. Ich frage, ob dies tatsächlich das gewünschte Verhalten ist.

Ich glaube nicht, dass es aus meiner Sicht eine vage Sorge ist. Ich möchte die Funktionsanfrage nicht falsch verstehen. Die genaue Angabe der Semantik würde mir also bei der Bewertung dieses Vorschlags helfen. Ein praktisches Problem ist beispielsweise, dass React heute während der Hydratation keine Attribute/Eigenschaften in der Produktion unterscheidet, da dies für keine der eingebauten Komponenten erforderlich ist. Aber es ist etwas, das wir für diesen speziellen Fall hinzufügen könnten, wenn es wirklich benötigt wird.

@gaearon

Ich denke, https://github.com/facebook/react/issues/11347#issuecomment -713230572 ist möglicherweise nicht das beste Beispiel, in dem diese Funktion benötigt wird.

Zumindest meiner Erfahrung nach ist das Festlegen von Eigenschaften anstelle von Attributen für einfachere Typen wie Zeichenfolgen, Zahlen oder Boolesche Werte nicht unbedingt erforderlich.

Der von Ihnen beschriebene Fall könnte leicht erreicht werden, indem Sie iconname direkt als Attribut verwenden, und es wäre immer noch weitgehend das gleiche gerenderte Endergebnis, ohne auf die Hydratation zu warten.

DieDie benutzerdefinierte Elementimplementierung wird geladen und registriert. Wenn ich das richtig verstehe, ist dies der Vorgang, der "Upgrade" genannt wird. Obwohl sein JS bereit ist, kann es immer noch nichts anzeigen, da wir den Symbolnamen nicht in den HTML-Code aufgenommen haben. Wir haben also nicht die Informationen, für welches Symbol gerendert werden soll. Dies liegt daran, dass wir zuvor gesagt haben, dass "nicht integrierte Eigenschaften aus dem HTML-Code entfernt werden können". Der Benutzer sieht also immer noch ein "Loch".

Was Sie hier erwähnen, können Sie je nach Implementierung der Komponente dieses Problem vermeiden, ein "Loch" zu sehen.

Dies könnte entweder durch einfaches Setzen von Standardeinstellungen in diesem Fall oder durch die Annahme eines Teils oder sogar des größten Teils des Inhalts durch Slots erreicht werden, sodass der Inhalt angezeigt wird, noch bevor die Komponente aktualisiert wird.


Ich denke, diese Funktion wäre vor allem für die Verwendung komplexerer Komponenten mit Eigenschaften wie Objekte, Arrays, Funktionen usw. nützlich.

Nehmen Sie als Beispiel die virtuelle Scroller-Komponente lit-virtualizer .

Damit dies richtig funktioniert, benötigt es ein items Array und eine renderItem Funktion und Sie können sogar optional ein scrollTarget Element festlegen, die alle nur in React mit Refs gesetzt werden können im Augenblick.

In einem Fall wie diesem würden Sie den Inhalt wahrscheinlich sowieso mit einer Art Paginierung oder Lazy Loading laden, sodass es möglicherweise nicht so problematisch ist, bis zum Hydratationsschritt eines SSR-Gehäuses keinen Inhalt zu haben.

@gaearon Bitte beachte, dass benutzerdefinierte Elemente ein DOM-Standard sind und es daher nicht per se eine "WC-Community" in dem Sinne gibt, dass jeder, der benutzerdefinierte Elemente verwendet, sie auf die gleiche Weise verwendet. Benutzerdefinierte Elemente werden für jeden Entwickler auf eigene Weise integriert, da sie von Natur aus ein Low-Level-Primitive sind, auf dem die Leute aufbauen.

Im Laufe der Zeit haben sich jedoch zahlreiche Muster herausgebildet und alle gängigen Frameworks (außer React) implementieren eine Lösung, um die Kompatibilität mit diesem DOM-Standard zu gewährleisten. Wie Sie auf https://custom-elements-everywhere.com/ sehen können , haben alle Frameworks/Bibliotheken (außer React) unterschiedliche Optionen gewählt. In dieser Ausgabe werden die gewählten Optionen als Optionen 1, 2 und 3 aufgeführt. Es gibt kein Framework/eine Bibliothek, die derzeit Option 4 oder 5 implementiert, und ich halte es nicht für wünschenswert, diese zu implementieren.

Daher schlage ich vor, dass React mit den anderen Frameworks/Bibliotheken nachzieht und eine Option wählt, die sich bewährt hat, zB aus den Optionen 1-3. Ich glaube nicht, dass React hier das Rad neu erfinden muss, indem es Option 4 oder 5 wählt, für die wir keine Daten haben, für die eine langfristig wartbare Lösung für React oder solche, die auf DOM-Standards aufbauen, ist.

Durch den Eliminierungsprozess (mit den unten verlinkten Kommentaren), da Option 3 nicht für JSX durchgeführt wird und die Optionen 4 und 5 von WC-Leuten in diesem Thread als unerwünscht markiert wurden, bleiben wir bei 1 oder 2 hängen .

Und da 1 scheinbar unhaltbare Nachteile hat, sieht es so aus, als ob Option 2 die richtige wäre? Gehen Sie einfach mit, wie Preact es macht?


Es gibt kein Framework/eine Bibliothek, die derzeit Option 4 oder 5 implementiert, und ich halte es nicht für wünschenswert, diese zu implementieren.

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

Ich halte es für ziemlich unwahrscheinlich, dass wir allein wegen dieser speziellen Funktion eine neue JSX-Syntax einführen würden.

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

Das würde mir ja Sinn machen. Danke für die Zusammenfassung! 😄

Zumindest meiner Erfahrung nach ist das Festlegen von Eigenschaften anstelle von Attributen für einfachere Typen wie Zeichenfolgen, Zahlen oder Boolesche Werte nicht unbedingt erforderlich. Der von Ihnen beschriebene Fall könnte leicht erreicht werden, indem einfach iconname direkt als Attribut verwendet wird, und es wäre immer noch weitgehend das gleiche endgültige gerenderte Ergebnis, ohne auf die Hydratation zu warten.

Ich verfolge nicht ganz. Mein hypothetisches Szenario war eine Antwort auf https://github.com/facebook/react/issues/11347#issuecomment -713223628, was wie ein Vorschlag klang, dass wir überhaupt keine Attribute im servergenerierten

  1. Was genau wird in dem von mir beschriebenen Szenario im servergenerierten HTML serialisiert
    A. Falls <github-icon iconname="smiley" />
    B. In dem Fall, den Sie angesprochen haben, zB <github-icon icon={{ name: "smiley" }} />
  2. Welche DOM-API auf dem Client während der Flüssigkeitszufuhr in einem dieser Fälle aufgerufen wird?

Vielen Dank!

Wurde es als Option in Betracht gezogen, ein Shim zu erstellen, das React.createElement() durch eine benutzerdefinierte Implementierung ersetzt, die darin eine Eigenschaftenzuordnung vornimmt?

Anwendungsbeispiel:

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

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

Entwurfsimplementierung:

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

Ich weiß, dass dies keine eingebaute Funktion ist, aber Benutzer mit sehr geringem Overhead entsperren sollte, denke ich.

@just-boris das ist im Grunde das, was Apps oder Bibliotheken jetzt tun, entweder mit einem createElement Patch oder einem Wrapper. Sie müssen auch die Eigenschaftsnamen mit Sonderzeichen in React ausschließen. Ich glaube nicht, dass es eine exportierte Liste gibt, also codieren sie einfach eine Denylist wie ['children', 'localName', 'ref', 'elementRef', 'style', 'className'] .

Da es ref-basiert ist, werden die Eigenschaften nicht auf dem Server festgelegt. Deshalb nenne ich die Sorge vage. Es gibt überhaupt keine Möglichkeit, Eigenschaften in React festzulegen, daher ist im Moment alles kaputt. Die verfügbaren Workarounds funktionieren bereits nur auf dem Client. Wenn eine Möglichkeit zum Festlegen von Eigenschaften auf dem Client und nicht auf dem Server integriert ist, wird SSR nicht plötzlich repariert oder unterbrochen.

Ich würde mich eigentlich mehr mit Ereignissen beschäftigen als mit SSR. React hat keine Möglichkeit, Ereignishandler hinzuzufügen, und das ist eine große Lücke in der Interoperabilität mit grundlegenden DOM-APIs.

Wurde es als Option in Betracht gezogen, ein Shim zu erstellen, das React.createElement() durch eine benutzerdefinierte Implementierung ersetzt, die darin eine Eigenschaftenzuordnung vornimmt?

Wie von Rob in der ursprünglichen Ausgabe erwähnt, habe ich mit diesem in der Vergangenheit über experimentierte https://github.com/skatejs/val , so eine benutzerdefinierte JSX Pragma hat , die Wraps React.createElement ein lebensfähig ist - möglicherweise eine kurzfristige Lösung.

Ich habe auch den WIP-Zweig von Skate, der speziell für React sowohl tiefe als auch flache SSR implementiert. Die bisherigen Erkenntnisse aus dieser Arbeit sind, dass Sie nicht einen einzigen JSX-Wrapper für alle Bibliotheken haben können, wenn Sie Deep SSR durchführen möchten, da jede Bibliothek dafür von ihren eigenen APIs und Algorithmen abhängig ist. (Für den Kontext hat Skate einen zentralen benutzerdefinierten Elementteil seiner Bibliothek und kleine Ebenen, die so gebaut sind, dass sie jede gängige Bibliothek auf dem Markt integrieren, um Verbrauch und Nutzung auf der ganzen Linie konsistent zu machen.)


Ich betreibe nicht mehr viel Open Source und mache auch kein Twitter mehr, also nimm meine Meinung mit einem Körnchen Salz.

Option 1 fühlt sich an, als ob sie als Advokat des Teufels für Option 2 existiert.

Option 2 fühlt sich wie der richtige Weg an. Es ist nah an dem, was Preact getan hat, also gibt es einen Präzedenzfall und die Möglichkeit eines Gemeinschaftsstandards (was meiner Meinung nach sowieso fantastisch wäre; JSX hat gezeigt, dass dies funktionieren kann), und es ist auch ein pragmatischer Schritt für React, da es einfach zu aktualisieren ist (im Gegensatz zu Option 1).

Option 3 sieht auf dem Papier gut aus, aber ich denke, logistisch wäre es aufgrund des kognitiven Overheads, der Dokumentation des Overheads und der Unbekannten mit SSR viel schwieriger als Option 2.

Wie bereits erwähnt, hatte ich ursprünglich Option 4 vorgestellt, aber nach Überlegung bin ich mir nicht sicher, ob ich sie mehr mag. Sie könnten es ändern, um mehr Opt-in zu sein, wobei Sie explizit props und events angeben (anstelle von attrs ), aber selbst dann erfordert Option 2 weniger Kenntnisse der Implementierungsdetails und es gibt nichts, was jemand ändern muss, um es anzunehmen.

Option 5 fühlt sich an, als würden wir das Kernproblem vollständig ignorieren.


Abgesehen davon freue ich mich sehr über die Traktion und hoffe, dass es euch allen gut geht!

Wenn es eine mögliche Lösung im Userland gibt, kann diese vorerst konsolidiert und von der Community empfohlen werden?

Es wäre viel einfacher, das React-Kernteam davon zu überzeugen, ein Feature hinzuzufügen, wenn es bereits als beliebtes und tragfähiges Modul von Drittanbietern existiert.

Wenn es eine mögliche Lösung im Userland gibt, kann diese vorerst konsolidiert und von der Community empfohlen werden?

Ich glaube nicht, dass dies etwas ist, das von außen [ordentlich] gepatcht werden kann. Die meisten Userland-Lösungen sind Wrapper wie diese und diese . Ohne React zu modifizieren, fällt mir keine vernünftige Alternative zu diesem Ansatz ein, und Wrapping ist definitiv keine ideale Lösung. 😕

Beachten Sie, dass Vue in vuejs/vue#7582 ein Siegel verwendet hat und "." als Präfix (nicht Glimmers "@").

Ich habe keinen Link zu einer maßgeblichen Aussage dazu (und vielleicht kann jemand, der Vue näher steht, dies ausdrücklich bestätigen), aber es scheint, dass Vue ab Vue 3.0 zu Option 2 gewechselt ist, was bedeutet, dass es sich ähnlich verhält wie Preact. (Siehe diese Ausgabe für den Kontext.)

Da es ref-basiert ist, werden die Eigenschaften nicht auf dem Server festgelegt. Deshalb nenne ich die Sorge vage. Es gibt überhaupt keine Möglichkeit, Eigenschaften in React festzulegen, daher ist im Moment alles kaputt. Die verfügbaren Workarounds funktionieren bereits nur auf dem Client. Wenn eine Möglichkeit zum Festlegen von Eigenschaften auf dem Client und nicht auf dem Server integriert ist, wird SSR nicht plötzlich repariert oder unterbrochen.

Können Sie bitte auf die spezifischen Fragen in https://github.com/facebook/react/issues/11347#issuecomment -713514984 antworten? Ich habe das Gefühl, wir reden aneinander vorbei. Ich möchte nur das gesamte beabsichtigte Verhalten verstehen – auf eine bestimmte Weise. Was wird wann gesetzt und was wird in welchem ​​Fall serialisiert.

Als zufälliger Preact+WC-Entwickler, der hier gestolpert ist, wollte ich darauf hinweisen, dass "Option 2" nicht nur eine _breaking change_, sondern auch eine _unvollständige Lösung_ ist (Preact kann immer noch nur deklarativ mit einer Teilmenge gültiger Webkomponenten interagieren).

Selbst als aktueller Nutzer der Heuristik klingt das auf Dauer kaum erstrebenswert.


Die Heuristik von Preact ("Option 2") kann keine Eigenschaften setzen, die in React eine besondere Bedeutung haben (Schlüssel, Kinder usw.) oder Preact (alles, was mit on beginnt).

Dh nach dem Rendern unter JSX:

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

Die Eigenschaften key , ongoing und children sind alle undefined (oder was auch immer der Standardwert ist) auf der erstellten Instanz von web-component .

Auch diese Option ist im Gegensatz zum OP ein Breaking Change. Die meisten Webkomponenten (zB mit lit-element ) bieten die Möglichkeit, serialisierte Rich Data über Attribute zu übergeben, um sie mit reinem HTML zu verwenden. Solche Komponenten können aus React so gerendert werden

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

die kaputt geht, wenn "Option 2" gewählt wird. Ich bin mir nicht sicher, wie häufig eine solche Verwendung von Webkomponenten in React ist, aber es gibt definitiv einige Codebasen, die dies tun, es ist weniger effizient als Refs, aber auch kürzer.

Schließlich steht es dem Autor von Web Component frei, Attribut und Eigenschaft mit demselben Namen subtil unterschiedliche Semantiken zuzuordnen. Ein Beispiel wäre eine Webkomponente, die das Verhalten eines nativen input Elements nachahmt – das Attribut value als Anfangswert behandelt und nur Änderungen an der Eigenschaft value . Solche Komponenten können mit dem heuristischen Ansatz nicht vollständig über jsx interagiert werden und könnten auch einen subtilen Bruch erfahren, wenn er eingeführt würde.

Ich halte es für ziemlich unwahrscheinlich, dass wir allein wegen dieser speziellen Funktion eine neue JSX-Syntax einführen würden.

Wie sieht es mit der Verwendung von Namensräumen aus? https://github.com/facebook/jsx/issues/66 schlägt vor, einen react Namensraum für key und ref hinzuzufügen. Wäre so etwas wie react-dom ein geeigneter Namespace, um DOM-Eigenschaften zu identifizieren? Unter Verwendung des Beispiels der Option 3 des OP wäre dies:

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

Das ist nicht das ergonomischste. Einfach dom wäre besser, aber ich bin mir nicht sicher, ob React Namensräume prägen möchte, die nicht mit react . Außerdem ist eine schlechte Ergonomie nicht das Ende der Welt, wenn die Einstellung als Eigenschaften der seltenere Fall sein sollte. Die Verwendung von Attributen ist besser, da dies Parität mit SSR bietet. Die Einstellung als Eigenschaften ist für problematische Attribute gedacht (z. B. um ein Objekt zu übergeben oder für Eigenschaften, die nicht von Attributen unterstützt werden), und genau für diese problematischen Eigenschaften können wir sie sowieso nicht mit Attributen verknüpfen und müssten sie hydratisieren über JS.

eine _unvollständige Lösung_

(Preact kann immer noch nur mit einer Teilmenge gültiger Webkomponenten deklarativ interagieren)

@brainlessbadger können Sie Ihren obigen Kommentar auch bearbeiten, um

@karlhorky Ich habe eine Erklärung hinzugefügt. Sorry, dass ich unnötig vage bin.

In Bezug auf Web Components-Ereignisse. Gibt es bereits einen Konsens darüber, wie eine zukünftige Blockierung durch kollidierende Ereignisnamen vermieden werden kann?

Dies ist auch für Attribute und Eigenschaften ein Problem, da neue Attribute wie alle diese zu HTMLElement hinzugefügt werden können: https://developer.mozilla.org/en-US/docs/Web/HTML/Global_attributes Sieht aus, als gäbe es ein paar auch neu hinzugefügt, so dass immer noch neue hinzugefügt werden.

Ich denke, streng genommen sollten Sie ein - im Namen eines benutzerdefinierten Attributs oder benutzerdefinierten Ereignisses verwenden.

Alles andere ist ein Kompromiss, der die Risiken abwägt, dass diese Komponente eine zukünftige Spezifikation daran hindert, einen schönen Namen zu verwenden.

Ereignisse machen mich jedoch besonders nervös, weil es nicht nur das Risiko ist, dass diese Komponente inkompatibles Verhalten zeigt. Wenn das neue Ereignis sprudelt, werden alle vorhandenen Webkomponenten in diesem Baumpfad ausgelöst.

Wenn benutzerdefinierte Attribute und Ereignisse erforderlich wären, um ein - in ihrem Namen zu verwenden, das als Heuristik verwendet werden könnte, um zu bestimmen, was verwendet werden soll. Dann würden wir die eingebauten spezialisieren. So wissen wir bereits, ob es sich überhaupt um ein benutzerdefiniertes Element handelt.

Eigenschaften könnten dann als Annehmlichkeiten verwendet werden, um eine schönere kurze Hand bereitzustellen. Zumindest können diese die eingebauten Eigenschaften abschatten und nicht mit zukünftigen Eigenschaften kollidieren.

Ich denke, streng genommen sollten Sie a - im Namen eines benutzerdefinierten Attributs oder benutzerdefinierten Ereignisses verwenden.

Die Spezifikation für benutzerdefinierte Elemente enthält keine derartigen Einschränkungen, sodass ihre Durchsetzung in React die Interoperabilität erheblich beeinträchtigt.

Von Autoren benutzerdefinierter Elemente sollte nicht erwartet werden, dass sie die willkürlichen Anforderungen eines Frameworks abonnieren. Stellen Sie sich vor, dass jedes Framework dies mit unterschiedlichen Meinungen und Konventionen tut. Dies verfehlt den Zweck der Verwendung von Webkomponenten. 😕

Idealerweise sollte sich das Framework an den Standard anpassen.

In der Tat. setAttribute() und dispatchEvent() sind nur nicht-webkomponentenspezifische APIs, die seit ihrer Einführung beliebige Namen zugelassen haben. Jedes Element kann jedes beliebige Attribut haben und jedes Ereignis auslösen - es ist nur eine Grundwahrheit des DOM.

Der Namespace data- wurde jedoch aus einem bestimmten Grund hinzugefügt. Es gibt einen Unterschied zwischen dem, was Sie technisch tun könnten, und der Best Practice. Sie können auch Prototypen technisch patchen, aber dann erhalten Sie enthält/beinhaltet Situationen.

Ich würde nicht vorschlagen, dass nur React dies hinzufügt, sondern dass die Community zu dieser Konvention wechselt, um den zukünftigen Namespace zu schützen. Ich finde es schade, dass dieses Thema nicht ernst genommen wird. Aber vielleicht ist das Web dazu verdammt, neuen APIs nur umständliche Namen hinzuzufügen.

Ich mag die Idee, Nicht-Builtins von SSR auszuschließen und bevorzuge Eigenschaften während der Hydratation, da mit deklarativem Schatten-DOM der eigentliche Inhalt im Inneren erweitert werden könnte.

Konkret glaube ich jedoch, dass dies bei den aktuellen Ansätzen, die die Leute mit SSR + AMP verwenden, Probleme verursachen wird, da dies Attribute verwendet und Sie davon ausgehen können, dass die AMP-Laufzeit bereits geladen wurde.

Die Tür Unter der Annahme geschlossen ist nicht auf der Option 3, die er einen erheblichen Vorteil gegenüber Option hat 2, die nicht in den obenen Nachteilen - Das Problem , das ich 2 mit Option gefunden habe , ist , dass , wenn Web - Komponentenbibliotheken asynchron geladen werden, könnte Vorgriff nicht wissen für ein unbekanntes Element, dass eine "Eigenschaft" eine Eigenschaft ist. Da es die im unbekannten Element vorhandene Eigenschaft nicht findet, wird standardmäßig ein Attribut verwendet. Eine Problemumgehung besteht darin, diese Komponente nicht zu rendern, bis die abhängigen Webkomponentenbibliotheken geladen sind, was nicht sehr elegant (oder in Bezug auf die Leistung) ist. Da meine Gruppe asp.net und nicht node.js verwendet, habe ich SSR nie wirklich untersucht, daher sind die folgenden Beobachtungen spekulativ.

Ich denke, es ist eine nette Geste seitens des React-Teams, vorzuschlagen, über das hinauszugehen, was andere Frameworks (meinem Wissen nach) unterstützen, bis hin zu SSR, um die Weitergabe von Objekteigenschaften während des anfänglichen Renderns zu ermöglichen, was der Benutzer besser.

Ich weiß nicht, ob ich das Offensichtliche sage oder nicht, aber ich denke, es besteht ein gewisser Konsens mit Webkomponenten, dass es für einige Eigenschaften praktisch ist, den Anfangswert des Attributs manchmal über eine JSON-Zeichenfolge festzulegen . in einfache Anführungszeichen gehüllt.

AMP verwendet jedoch eine andere Konvention – es verwendet dazu script type=application.json , was eine vernünftige (aber ausführliche) Alternative ist.

Aber bleiben Sie beim Attributansatz:

Die Webkomponentenbibliothek kann dann das Attribut parsen oder den Wert über eine Eigenschaft übergeben.

Um Verzögerungen bei der Flüssigkeitszufuhr zu vermeiden, wäre es praktisch, Folgendes einzustellen:

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

während der SSR. Dann könnte github-icon sofort sein Ding machen, intern eine JSON.parse des Attributs durchführen und nicht warten müssen, bis React den (vollständigeren?) Objektwert übergibt.

Jetzt haben wir also eine knifflige Situation – wenn wir auf dem Server rendern, möchten wir, dass "icon" als Attribut behandelt wird, aber den Wert als Objekt übergeben können, was idealerweise der SSR-Mechanismus in das Attribut einreihen würde. Auf dem Client möchten wir in der Lage sein, das Objekt direkt zu übergeben und nicht den Aufwand für das Stringifizieren und Parsen zu durchlaufen.

Natürlich sind einige Eigenschaften Objekte, die nicht für das Stringifizieren/Parsen geeignet sind, daher trifft dies nicht zu, und diese Eigenschaften müssen warten, bis die Hydratation abgeschlossen ist.

Wenn alle Eigenschaften und Attribute der bevorzugten Namenskonvention des React-Teams (vielleicht) folgten – alle Attribute hatten Bindestriche und alle Eigenschaften mit camelCase zusammengesetzte Namen, könnte die Existenz eines Bindestrichs vielleicht helfen, zwischen Attributen und Eigenschaften zu unterscheiden:

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

Das Problem ist, dass nichts in der obigen Syntax angibt, was auf dem Server im Vergleich zum Client zu tun ist, und idealerweise könnten wir eine Bindung für beide verwenden, und React wäre schlau genug, um herauszufinden, welche verwendet werden soll.

React könnte einfach davon ausgehen, dass ein Attribut-/Eigenschaftsschlüssel in der Groß-/Kleinschreibung immer als Objekt auf der Clientseite übergeben wird und eine JSON-stringifizierte Serialisierung des Objekts, wenn es während der SSR festgelegt wird. Ebenso könnten Bindestriche im Schlüssel (sicher, denke ich) davon ausgehen, dass es sich um ein Attribut handelt, und einfach die .toString() des Werts übergeben. Aber ich denke, das setzt zu viel voraus. Und die Nichtunterstützung von Einzelwortattributen und -eigenschaften, die als gültiges HTML angesehen werden, wenn die Attribute auf eine Webkomponente angewendet werden, die HTMLElement erweitert, wäre zu eng. Ich bin dafür, dass das W3C eine Liste von "reservierten" Attributnamen herausgibt, die es in Zukunft verwenden könnte, ähnlich wie reservierte Schlüsselwörter für JS, und Frameworks, die Wege finden, Entwickler zu warnen, wenn sie einen reservierten Attributnamen falsch verwenden.

Aber ich denke, Option 3 ist der beste Ansatz. Wenn es jedoch, wie von @gaearon vorgeschlagen , für eine noch bessere Benutzererfahrung verbessert werden kann, wäre das großartig.

Mein Vorschlag wäre:

<github-icon icon={myDynamicIcon}/>

bedeutet Attribut (toString()).

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

würde bedeuten -- während SSR ignorieren, aber den Client an die Objekteigenschaft binden (nach dem Hydratisieren).

Was ist nun mit dem Szenario (einige des?), an dessen Lösung das React-Team interessiert ist? Mein erster Gedanke war nur ein anderes Siegel, wie zum Beispiel zwei Doppelpunkte:

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

würde bedeuten, dass während SSR die Eigenschaft JSON.stringify, als Attribut im gerenderten HTML festgelegt und als Objekt an den Client übergeben wird, wenn sich die Eigenschaft, an die sie gebunden ist, nach der Hydratation ändert.

Damit bleibt die schwierige Situation, was mit zusammengesetzten Namen zu tun ist. Dh wenn wir setzen:

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

in der Vergangenheit war es nicht umstritten, dass das entsprechende Attribut für den Eigenschaftsnamen iconProps icon-props sein sollte.

Vielleicht ist dies kontroverser geworden, weil man befürchtet, dass einige dieser Webkomponenten ohne Änderungen in die Plattform integriert werden könnten, auf der Attribute keine Bindestriche haben dürfen (aber Eigenschaften können camelCase sein). Meines Wissens gibt es noch kein natives Element, das die Übergabe komplexer Objekte über die JSON-Deserialisierung ermöglicht, aber ich würde mich nicht wundern, wenn dies in Zukunft erforderlich ist. Wie würde React also wissen, wann Bindestriche eingefügt werden müssen oder nicht?

Der einzige (hässliche?) Vorschlag, den ich machen kann, ist:

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

Das bedeutet, dass auf dem Server das Attribut icon-props nach der Serialisierung verwendet wird und auf dem Client die Eigenschaft iconProps verwendet wird, um Objekte direkt zu übergeben.

Ein weiterer (weitgehender?) potenzieller Vorteil der ausführlicheren Notation, die ein hybrides Attribut/Eigenschaftspaar ermöglicht, ist folgender: Es könnte etwas schneller sein, die Eigenschaften eines nativen Elements anstelle des entsprechenden Attributs wiederholt festzulegen, insbesondere für Eigenschaften, die keine Zeichenfolgen sind. Wenn ja, verwendet React derzeit Attribute auf dem Server und Eigenschaften auf dem Client? Wenn nicht, liegt es an dem gleichen Problem der Benennung von Übersetzungsschwierigkeiten, das diese Notation lösen würde?

@bahrus Ich denke, es kann vereinfacht werden

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

Ich denke, es ist vielleicht am besten, das Problem an einem konkreten Beispiel zu veranschaulichen.

Das HTML-Formularelement hat eine Reihe von Attributen. Die mit "zusammengesetzten Namen" scheinen nicht alle einem konsistenten Namensmuster zu folgen - im Allgemeinen bleiben alle Kleinbuchstaben, keine Trennzeichen, aber manchmal gibt es einen Bindestrich - "accept-charset" zum Beispiel, manchmal nicht - - "novalisieren".

Der entsprechende JS-Eigenschaftsname passt auch nicht in ein schönes universelles Muster -- acceptCharset, noValidate. Die noValidate-Eigenschaft / das novolidate-Attribut ist ein boolescher Wert, daher wäre es für den Client verschwenderisch (ich stelle mir vor), myForm.setAttribute('novalidate', '') im Gegensatz zu myForm.noValidate = true zu verwenden.

Auf dem Server ist es jedoch nicht sinnvoll, myForm.noValidate = true zu setzen, da wir eine String-Darstellung des DOM senden möchten, also müssen wir das Attribut verwenden:

<form novalidate={shouldNotValidate}>

Tatsächlich ist mir nicht klar, dass React/JSX eine universelle Möglichkeit hat , ein boolesches Attribut bedingt festzulegen, möglicherweise basierend auf einer

Wie können wir all diese Szenarien abbilden, damit der Entwickler die volle Kontrolle über Server (über Attribute) und Client (über Eigenschaften) hat, ohne an Leistung einzubüßen? In diesem Fall eines booleschen Wertes wie novalidat würde ich vorschlagen:

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

Wenn React das DOM, so chaotisch es auch sein mag, vollständig und so performant wie möglich unterstützen würde, würde dies meiner Meinung nach einen großen Beitrag zur Unterstützung benutzerdefinierter Elemente leisten, die wahrscheinlich auch in den Benennungsmustern nicht sehr konsistent sind.

Das Hinzufügen von Unterstützung für die optionale Serialisierung von Objekteigenschaften zu Attributen über JSON.stringify auf dem Server wäre eine zusätzliche große Win-Win-Situation für React und Webkomponenten, wie mir scheint.

Oder übersehe ich etwas? ( @eavichay , wie würden Sie vorschlagen, dass diese Szenarien am besten dargestellt werden, ohne Ihrem Beispiel zu folgen).

War diese Seite hilfreich?
0 / 5 - 0 Bewertungen