Ember.js: [META] Stickbereitschaft

Erstellt am 18. Aug. 2020  ·  17Kommentare  ·  Quelle: emberjs/ember.js

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.

Technische Voraussetzungen

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".

MVP: Veraltet und ersetzt (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 als this.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:

  • [] Entwerfe und veröffentliche eine neue Version von (component) , die nur statische Zeichenfolgen unterstützt (erfordert RFC).
  • [] Behebung des Fehlers, durch den sich der Aufruf von spitzen Klammern wie der Aufruf dynamischer Komponenten in Ember verhält (Bugfix, möglicherweise eine intime API)

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.

Nächste Schritte

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.

Help Wanted

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 die import oder (component "staticString") benötigen, um irgendwohin zu gehen.

Zu den Optionen gehören:

  • Der Aufrufer der Komponente führt (component "staticString")
  • Der Code mit der Dynamik listet die Möglichkeiten auf, fügt sie in eine Karte ein und verwendet (get componentMap dynamicString) , um sie herauszuholen

Jetzt 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.

Alle 17 Kommentare

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:

  • Der Aufrufer der Komponente führt (component "staticString")
  • Der Code mit der Dynamik listet die Möglichkeiten auf, fügt sie in eine Karte ein und verwendet (get componentMap dynamicString) , um sie herauszuholen

Jetzt 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.

  1. Vorlagenimporte
  2. Verwenden von Komponentenklassen als Invokables

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.

Verwenden von Komponentenklassen als Invokables

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:

  1. Derzeit verwenden wir die JS-Lösung @wycats, auf die hingewiesen wurde. Dasselbe in einem HBS-Anbieter zu tun, ist machbar, aber wie wir alle wissen, ist die Logik in HBS etwas schwieriger. Es ist auch sehr viel schwieriger zu testen, ob eine Anbieterkomponente die richtige Komponente liefert als eine util-Funktion, die die richtigen Zeichenfolgen zurückgibt.
  2. 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.

War diese Seite hilfreich?
0 / 5 - 0 Bewertungen