Heute kann Embroider im Kompatibilitätsmodus in neuen Anwendungen und in vielen vorhandenen Anwendungen verwendet werden. Es ist schwieriger, Embroider im Modus staticComponents
zu verwenden. Dies ist erforderlich, um den Vorteil des Modus splitAtRoutes
.
Problem Nr. 501 im Embroider-Repository verfolgt die verbleibenden Probleme, die zur Stabilisierung von Embroider als Teil einer Ember.js-Version erforderlich sind.
In dieser Ausgabe werden die Schritte beschrieben, die erforderlich sind, bevor Benutzer Ember mit Embroider als unterstützte Option für die routenbasierte Codeaufteilung ("Stickbereitschaft") praktisch verwenden können. Es gibt zwar viele detaillierte Möglichkeiten zur Verwendung von Embroider (einschließlich eines Kompatibilitätsmodus, der nur wenige konkrete Vorteile bietet, aber für die Migration von Ember selbst auf Embroider standardmäßig wichtig ist). Dieses Problem konzentriert sich jedoch auf die Möglichkeit, Embroider mit einer normalen Ember-Anwendung zu verwenden und Vorteile zu erzielen von der routenbasierten Codeaufteilung.
Wie in der Embroider-README-Datei beschrieben , muss eine Anwendung in der Lage sein, diese Flags zu aktivieren, um die splitAtRoutes
) zu aktivieren:
staticAddonTestSupportTrees
staticAddonTrees
staticHelpers
staticComponents
Wenn ein Addon oder eine Anwendung bei Vorhandensein dieser Flags nicht funktionieren kann, verwenden sie "klassische dynamische Funktionen".
(component dynamicString)
Für das erste Ziel der Embroider-Bereitschaft ("MVP") müssen wir die häufigsten Hindernisse beseitigen, die wir beim Aktivieren der statischen Flags in realen Anwendungen festgestellt haben.
Für den MVP-Meilenstein ist es kein Ziel, dass alle Ökosystem-Addons diese Flags unterstützen.
Stattdessen besteht das Ziel darin, dass Anwendungen einen angemessenen Übergangspfad zu splitAtRoutes
haben und dass in diesem Modus umfangreiche, nicht triviale Anwendungen erstellt werden können. Das bedeutet, dass alle im Standardentwurf enthaltenen Addons von den klassischen dynamischen Funktionen abgewandert sein müssen. Dies bedeutet auch, dass Addons, die häufig in realen Anwendungen wie Ember Concurrency verwendet werden, keine klassischen dynamischen Funktionen verwenden dürfen.
staticComponents
Dies ist das wichtigste statische Flag , und seine Anforderung stellt das größte Hindernis für das MVP-Ziel dar.
Um staticComponents
zu aktivieren, muss eine Anwendung ( einschließlich ihrer Addons ) frei von jeglicher Verwendung von (component dynamicString)
.
Wichtig ist , dass Anwendungen und deren Addons benutzen (component "static string")
in staticComponents
- Modus.
In der Praxis , dass diese Mittel müssen wir von Mustern wandern weg wie folgt :
{{#let (component this.componentName) as | Component |}}
Addons, die derzeit Zeichenfolgen als Teil ihrer öffentlichen API verwenden, müssen stattdessen Komponenten verwenden. Dies bedeutet, dass dieses Addon zu einem Ansatz migriert werden muss, bei dem die Benutzer die aufzurufende Komponente anstelle einer Zeichenfolge angeben müssen.
Dies ist eine besonders heikle Situation, da
this.component
alsthis.componentName = <code i="29">scaffolding/${dasherize(csId!)}/${dasherize(this.args.feature)}</code>
. Situationen wie diese sind genau der Grund, warum wir über eine Übergangsstrategie nachdenken und sie sorgfältig ausarbeiten müssen.
Damit Addons von (component dynamicString)
weg migrieren können, müssen wir mindestens eine neue Version des Schlüsselworts component
erstellen, die den Aufruf dynamischer Komponenten nicht zulässt.
Außerdem müssen wir einen Fehler beheben, durch den der Aufruf von Komponenten mit spitzer Klammer-Syntax versehentlich genauso funktioniert wie das dynamische Schlüsselwort component
. Dies muss bald geschehen, denn sobald die Leute versuchen, von (component dynamic)
wegzuwandern, besteht eine hohe Wahrscheinlichkeit, dass die Leute versehentlich auf <dynamic>
migrieren, beobachten, dass es funktioniert, und weitermachen.
Aktionselemente:
(component)
, die nur statische Zeichenfolgen unterstützt (erfordert RFC).staticHelpers
Das Flag staticHelpers
verringert nicht die Ausdruckskraft von Ember-Vorlagen, bedeutet jedoch, dass die gesamte Liste der Helfer einer Anwendung nicht in den Modulen im Loader verfügbar ist.
Wir gehen davon aus, dass dies brechende Auswirkungen auf Ember-Anwendungen haben wird, aber viel weniger Probleme als staticComponents
. Wir erwarten derzeit nicht, dass staticHelpers
das MVP-Ziel beeinflusst, aber das kann sich ändern, wenn wir versuchen, Anwendungen auf splitAtRoutes
zu aktualisieren. In diesem Fall müssen wir die problematischen Anwendungsfälle bewerten und neue APIs in Betracht ziehen, um die Migration zu erleichtern.
staticAddonTrees
und staticAddonTestSupportTrees
Die aktuelle Annahme ist, dass diese Flags mit den meisten idiomatischen Ember-Apps und Addons kompatibel sind und daher keine wesentlichen Probleme für die Anforderungen des MVP-Ziels darstellen.
Dieses Problem soll als Tracking-Problem bleiben, nachdem wir das MVP-Ziel erreicht haben. Nach dem MVP-Ziel müssen wir eine vollständige Migration des Ökosystems ermöglichen und die Ergonomie dynamischer Anwendungsfälle verbessern (die die Code-Aufteilung weiterhin unterstützen). Vorlagenimporte dürften dazu beitragen, diese Ziele zu erreichen.
Eine Möglichkeit, die Übergangskosten zu senken, besteht darin, Addons zu ermöglichen, statisch anzugeben, welches ihrer Argumente einer statischen Zeichenfolge entspricht, die vom Aufrufer der Komponente übergeben wird
Als Spitball (wobei better-component
ein Platzhalter für den Namen des statischen Komponentenschlüsselworts ist).
{{better-component <strong i="8">@arg</strong> staticString=true}}
Auf diese Weise können Addons angeben, dass ein bestimmter "dynamischer Aufruf" nur funktioniert, wenn der Aufrufer eine statische Zeichenfolge bereitstellt.
Wir sollten dies nur tun, wenn viele dynamische Komponentenfälle in der Praxis durch ein direktes "an ein Addon übergebenes String-Argument" verursacht werden.
Idk, wenn dies ein Thema für dieses Problem ist, aber ich habe regelmäßig versucht, die vollständige Statik von Emberclear herauszufinden, und habe eine Papierspur von Problemen und deren Lösungen geführt.
https://github.com/NullVoxPopuli/emberclear/pull/784
Wenn also Leute auf Probleme stoßen, kann vielleicht die Abe-Dokumentation helfen? Idk
Außerdem freue ich mich sehr auf das Sticken und habe bereits große Pläne, sobald die volle Statik erreicht ist
https://github.com/emberjs/rfcs/issues/611
Unsere Anwendung ist ein dynamisches System zur Bereitstellung von Inhalten. Inhaltsersteller setzen Inhalte aus "Aktivitätselementen" zusammen, von denen jedes eine entsprechende Ember-Komponente hat, deren Name in einem Ember-Datenmodell definiert ist. Das Herz dieses Systems sieht mehr oder weniger so aus:
// example model definition
export default class TextElement extends Model {
_componentName = 'text-element';
}
{{#each (sort-by "position" @activityElements) as |activityElement|}}
{{component (get activityElement "_componentName")}}
Meine Lektüre des oben Gesagten legt nahe, dass dies ein (component dynamicString)
-Szenario ist. Ist das richtig?
Meine Lektüre des oben Gesagten legt nahe, dass dies ein Szenario (Komponente dynamicString) ist. Ist das richtig?
Was ist @activityElements
und wo ist es definiert?
Dies wäre ein Array von Ember Data Model-Instanzen, die an eine Controller- Komponente übergeben werden.
Wir haben auch ein wenig stark in dynamische Komponenten investiert, FYI. Ich habe hier letztes Jahr danach gefragt: https://discuss.emberjs.com/t/the-perils-of-dynamic-component-invocation/16784.
Wir auch. Wir kennen die Komponenten, die in der App verwendet werden sollen, buchstäblich nicht im Voraus. Sie befinden sich in einer Datenbank (was sie natürlich veränderbar macht), werden im Backend kompiliert und bei Bedarf an das Frontend gesendet. Nicht in der Lage zu sein, den dynamischen component
-Helfer für uns zu verwenden, ist dasselbe wie nicht für jeden über ein Array. Ich hoffe wirklich, dass es für Anwendungsfälle wie diesen einen Weg nach vorne gibt. Ich würde mich definitiv nicht um Code-Splitting / Tree Shaking kümmern, wenn meine App nicht funktioniert, weil ich nicht die Dynamik habe, die ich brauche.
Könnte eine Karte aller gültigen Komponenten für dieses Szenario verwendet werden?
Zum Beispiel:
{{#let (hash
Foo=(import 'path/to/foo')
Etc=...
) as |validComponents|
}}
{{component (get validComponents @someDynamicValue)}}
{{/let}}
?
Sie können nicht wirklich über vollständige dynamische Komponenten verfügen, da Sie nur das rendern können, was in Ihrer App enthalten ist. Das Erstellen einer Liste zur Auswahl des zu rendernden Elements würde auch beim Debuggen helfen. "Oh, dieser Wert war keine der gültigen Komponenten"
Könnte eine Karte aller gültigen Komponenten für dieses Szenario verwendet werden?
Dies würde definitiv unseren Anwendungsfall abdecken.
@NullVoxPopuli @jherdman Super !
Im Allgemeinen ist die Art und Weise, darüber nachzudenken, was die Leute anstelle von (component dynamicString)
tun müssten, dass sie die import
oder (component "staticString")
benötigen, um irgendwohin zu gehen.
Zu den Optionen gehören:
(component "staticString")
(get componentMap dynamicString)
, um sie herauszuholenJetzt denken Sie vielleicht: Wie könnte (get components dynamicString)
besser sein als (component dynamicString)
? Die Antwort ist, dass componentMap
irgendwo erstellt werden musste und die Regeln rekursiv gelten.
Angenommen, Sie schreiben eine Komponente, mit der Sie <InputField @type="text" />
oder <InputField @type="checkbox" />
schreiben können (wie <input>
in HTML).
Ihre Vorlage für input-field
würde ungefähr so aussehen:
{{#let (hash text=(component "inputs/text-field") checkbox=(component "inputs/checkbox")) as |components|}}
{{component (get components @type)}}
{{/let}}
Wenn Sie den Code auf diese Weise schreiben, kann Embroider erkennen, dass nur zwei Komponenten im Bundle enthalten sein müssen. Hattest du es so geschrieben:
{{component (concat "inputs/" <strong i="33">@type</strong> "-field")}}
(Ja, solche Dinge sind überraschend häufig)
Dann kann Embroider den Code nicht einfach analysieren und die Komponenten, die im Bundle enthalten sein werden, einschränken. Dies ist noch schlimmer, wenn Sie die Arbeit in JavaScript erledigt haben (was auch sehr häufig ist):
export default class extends Component {
get innerComponent() {
return `inputs/${this.args.type}-field`;
}
}
mit dieser Vorlage:
{{component this.innerComponent}}
Es ist eine kleine Änderung, aber es ermöglicht Embroider zu bestimmen, welche Komponenten tatsächlich verwendet werden.
Ich möchte auch die Verbindungen zu zwei anderen Funktionen während des Flugs genauer erläutern.
Wenn wir das vorherige Beispiel mit diesen beiden Funktionen neu schreiben, sieht es so aus:
---
import TextField from "./text-field";
import Checkbox from "./checkbox";
---
{{#let (hash text=TextField checkbox=Checkbox) as |components|}}
{{component (get components @type)}}
{{/let}}
Dies hat die nette Eigenschaft, eine spezielle Regel zu eliminieren, die Embroider verstehen müsste, und das Benutzermodell für die Code-Aufteilung noch mehr über Module zu machen.
Seit RFC # 481 ist eine Komponentenklasse eine vollständig eigenständige Einheit, die alle Informationen enthält, die die Glimmer-VM benötigt, um sie als Komponente aufzurufen.
Hinweis: Dies gilt nicht nur für Klassen, die von
@ember/component
oder@glimmer/component
erben. Seit RFC 481 ist Embers Definition von "Komponente" "ein Objekt, das einer Vorlage und einem Komponentenmanager zugeordnet ist", einschließlich benutzerdefinierter Komponenten wie Hook-Komponenten .
Seit RFC # 432 (kontextbezogene Helfer und Modifikatoren) und RFC # 496 (strikter Lenker-Modus) lautet das Design für die Zukunft der Ember-Vorlagensyntax: Ausdrücke, die Helfer, Komponenten oder Modifikatoren enthalten, können als Helfer, Komponenten oder Modifikatoren aufgerufen werden.
Die aktuelle Sammlung genehmigter RFCs impliziert, dass <SomeComponentClass />
(oder <this.componentClass>
wobei this.componentClass
in eine Komponentenklasse aufgelöst wird) "einfach funktionieren" würde. Es ist auch der Plan für die Implementierung.
Aus Gründen der Übersichtlichkeit sollten wir jedoch einen neuen RFC erstellen, der dieses Verhalten explizit spezifiziert.
Es ist für uns machbar, irgendwo eine Liste gültiger Komponenten in HBS anzugeben, aber es gibt ein paar Vorsichtsmaßnahmen, die es wert sind, erwähnt zu werden:
Dies gibt es in JS (mit öffentlicher API sowieso) afaik nicht, aber die Verwendung der Verzeichnisstruktur zu unserem Vorteil wäre hier sehr schön. Z.B:
{{#let (lookup-directory "components/inputs/") as |components|}}
{{/let}}
Diese Art von Dingen kann im Laufe der Zeit einfacher zu warten sein, wenn Sie bekannte polymorphe Typen haben, die nach Verzeichnisstruktur gruppiert sind.
Auf jeden Fall habe ich das Gefühl, dass dynamische Komponenten ein eigenes Thema benötigen, um dieses zu diskutieren, anstatt dieses zu übernehmen 🙈 😄
Auf jeden Fall habe ich das Gefühl, dass dynamische Komponenten ein eigenes Thema benötigen, um dieses zu diskutieren, anstatt dieses zu übernehmen 🙈 😄
Ich bin damit einverstanden und werde in Kürze einen im RFC-Repo öffnen und hier einen Link posten.
@mehulkar @wycats @jherdman Wie wäre es, wenn Benutzer nur ihre Liste dynamisch aufrufbarer Komponenten importieren könnten? Scheint viel einfacher zu folgen als ein Helfer, der eine zusätzliche Indirektionsebene enthält.
import Component1 from './dynamic/component-1';
import Component2 from './dynamic/component-2';
import Component3 from './dynamic/component-3';
export default class extends Component {
get innerComponent() {
switch(this.args.type) {
case 'one':
return Component1;
case 'two':
return Component2;
case 'three':
return Component3;
default:
// handle invalid type
}
}
}
Dann kann die Vorlage einfach Folgendes tun: {{#let (component this.innerComponent) as |DynamicComponent|}}
oder ähnliches.
Ich würde so etwas sehr bevorzugen, das einfacher zu suchen / lesen / analysieren ist und leicht erkennt, wo diese Komponenten verwendet werden. Gedanken?
@ Samsinite Ja, ich denke, das ist die Idee. Der Helfer in HBS wäre meistens syntaktischer Zucker und nützlich für reine Template-Komponenten.
@Samsinite Das ist im Grunde das, was ich als kurzfristige Lösung vorgeschlagen habe (vor dem Import von Vorlagen).
Es würde erfordern Komponentenklassen zu ermöglichen aufrufbaren zu sein, was was ich über im Gespräch dieses Kommentar .
Lass es uns tun!
Ah, das macht nur für Vorlagenkomponenten Sinn, entschuldigen Sie das Missverständnis :). Ich mag auch die Syntax für Vorlagen nur Komponenten von:
---
import TextField from "./text-field";
import Checkbox from "./checkbox";
---
{{#let (hash text=TextField checkbox=Checkbox) as |components|}}
{{component (get components @type)}}
{{/let}}
Außerdem würde ich es wahrscheinlich nur für Vorlagen und von js unterstützte Komponenten verwenden, je nachdem, was andere im Team leichter zu lesen finden.
Hilfreichster Kommentar
@NullVoxPopuli @jherdman Super !
Im Allgemeinen ist die Art und Weise, darüber nachzudenken, was die Leute anstelle von
(component dynamicString)
tun müssten, dass sie dieimport
oder(component "staticString")
benötigen, um irgendwohin zu gehen.Zu den Optionen gehören:
(component "staticString")
(get componentMap dynamicString)
, um sie herauszuholenJetzt denken Sie vielleicht: Wie könnte
(get components dynamicString)
besser sein als(component dynamicString)
? Die Antwort ist, dasscomponentMap
irgendwo erstellt werden musste und die Regeln rekursiv gelten.Angenommen, Sie schreiben eine Komponente, mit der Sie
<InputField @type="text" />
oder<InputField @type="checkbox" />
schreiben können (wie<input>
in HTML).Ihre Vorlage für
input-field
würde ungefähr so aussehen:Wenn Sie den Code auf diese Weise schreiben, kann Embroider erkennen, dass nur zwei Komponenten im Bundle enthalten sein müssen. Hattest du es so geschrieben:
(Ja, solche Dinge sind überraschend häufig)
Dann kann Embroider den Code nicht einfach analysieren und die Komponenten, die im Bundle enthalten sein werden, einschränken. Dies ist noch schlimmer, wenn Sie die Arbeit in JavaScript erledigt haben (was auch sehr häufig ist):
mit dieser Vorlage:
Es ist eine kleine Änderung, aber es ermöglicht Embroider zu bestimmen, welche Komponenten tatsächlich verwendet werden.