Übergeordnete Kommunikation in Elm: OutMsg vs Übersetzer vs NoMap-Muster

Wenn Ihre App Elm wächst, möchten Sie sie in kleinere Teile zerlegen, um skalieren zu können. Ich habe dies in einem anderen Blogpost behandelt: Strukturiertes TodoMVC-Beispiel mit Elm.

Teil, wenn diese Skalierung möglicherweise das Senden einer Nachricht von einem Modul an das übergeordnete Modul erfordert, z. B. wenn Navigationsschaltflächen in einer bestimmten Ansicht eine Nachricht an den Router der obersten Ebene senden müssen.

Nach einiger Zeit bemerkte ich 3 verschiedene Muster für die Behandlung dieses Problems und überarbeitete Elm TodoMVC für all diese unterschiedlichen Vorgehensweisen in diesem Repository, damit Sie sie nebeneinander vergleichen können.

Das OutMsg-Muster

Ich glaube, Folkertdev war der erste, den ich in Elm über Kind-Eltern-Kommunikation gesehen habe. Sein Blogpost erklärt diesen Ansatz ziemlich gut.

Zusammenfassend können Sie jedoch einen zusätzlichen Wert in Ihrer Aktualisierungsfunktion zurückgeben. Anstatt dies zurückzugeben:

(Modell, Cmd Msg)

Sie geben dies zurück:

(Modell, Cmd Msg, OutMsg)

In diesem Fall ist die übergeordnete Aktualisierungsfunktion für deren Behandlung verantwortlich. Auf diese Weise muss das Kind nichts über sein Elternteil wissen, aber das Elternteil muss über die OutMsgs seines Kindes Bescheid wissen.

Ich habe TodoMVC mit diesem Ansatz implementiert. Wenn Sie dies jedoch im realen Maßstab überprüfen möchten, hat Richard Feldman das Ulmen-Spa-Beispiel auf diese Weise implementiert.

Ein weiteres Beispiel, das diesen Ansatz verwendet, ist Elm-Datepicker.

Das Übersetzermuster

Das Übersetzermuster ist sehr ähnlich zu dem von OutMsg, aber anstatt dass der Elternteil die Nachrichtentypen des Kindes kennt, ist es der Elternteil, der übergibt, welche Nachrichten über einen Übersetzer generiert werden. Alex Lew erklärt seinen Ansatz hier viel besser.

Grundsätzlich haben Sie einen Übersetzer, der wie folgt aussieht:

Typ alias TranslationDictionary msg =
  {onInternalMessage: InternalMsg -> msg
  , onPlayerWin: Int -> msg
  , onPlayerLose: msg
  }

Mit diesem Ansatz habe ich auch TodoMVC implementiert, und ich glaube, elm-autocomplete ist auch ein gutes Beispiel.

Das elm-parent-child-update ist eine Bibliothek, die Ihnen bei der Aktualisierung von Child-Parent hilft, die diesem Muster zu folgen scheint.

Das NoMap-Muster

Mir ist aufgefallen, dass ich das getan habe. Die Grundidee ist, Cmd.map und Html.map zu vermeiden. Stattdessen muss jeder dieselbe Sprache sprechen. Mit anderen Worten, Ihre Aktualisierungs- und Anzeigefunktionen müssen den Msg-Typ der obersten Ebene zurückgeben.

Damit haben Sie wahrscheinlich Nachrichten wie "MsgForLogin", "MsgForRouter" usw., also würden Sie in Ihrer Ansicht etwas tun wie:

button [onClick (MsgForLogin SignUp)] []

Auf diese Weise habe ich TodoMVC zum ersten Mal überarbeitet. Als ich OutMsg zum ersten Mal sah, verstand ich den Grund dafür nicht, weil ich meine Nachrichten nicht zugeordnet habe.

Schauen Sie sich die Lightning-Talk-App an, um ein größeres Beispiel für diesen Ansatz zu erhalten. Außerdem scheint diese App Kris Jenkins 'Art zu folgen, Elm-Apps zu strukturieren, was diesen Ansatz begünstigt, da er die Msgs-Typen in einer Types.elm-Datei trennt.

In der elm-taco-Bibliothek wird eine Mischung aus OutMsg- und NoMap-Mustern verwendet, indem ein Taco der obersten Ebene bereitgestellt wird, an den Sie Nachrichten senden können.

Beobachtungen und Vergleiche

Während der Recherche und Umgestaltung dieser Muster habe ich einige Dinge festgestellt, die je nach Ihren Bedürfnissen Vor- oder Nachteile haben können:

  • In NoMap bleibt die Aktualisierungsfunktion der Eltern in etwa gleich wie Ihre App wächst, während in OutMsg und Translate die Aktualisierungsfunktion der Eltern möglicherweise sehr umfangreich wird, da Sie die OutMsg jedes Kindes verarbeiten müssen (Beispiel).
  • In OutMsg und Translate müssen die verschachtelten Module nichts von übergeordneten Modulen importieren, wodurch sie besser gekapselt werden. Es wäre einfacher, ein Teilmodul beispielsweise als Bibliothek zu extrahieren und zu veröffentlichen
  • Damit NoMap funktioniert, sollten Ihre Nachrichten in einer von Update getrennten Datei gespeichert sein. Andernfalls besteht eine Abhängigkeitsschleife. Dies ist eine gute Sache, die Sie zwingt, Dinge aufzuteilen, aber auch eine schlechte, wenn Sie für jedes Modul eine einzige Datei haben möchten (Home.elm, Login.elm, Router.elm).
  • In NoMap ist es einfacher, Nachrichten an einen anderen Ort zu senden, es ist jedoch möglicherweise schwieriger, alle dadurch verursachten Statusänderungen zu verfolgen.
  • Gemessen zum Zeitpunkt dieses Schreibens hat der NoMap-Ansatz für die TodoMVC-Refaktoren 546 LOC, OutMsg 561 und Translator 612, wenn dies für Sie von Bedeutung ist
  • In NoMap müssen Sie eventuell den _ catch-all-Fall verwenden, um Nachrichten von anderen Stellen zu ignorieren, die Sie nicht bearbeiten möchten. Der Compiler hilft also weniger. Er kann nicht erkennen, was Sie vermissen dass auf Ulme schlaff)
  • In OutMsg und Translator können Sie sich nur die Typen oder Übersetzer ansehen, um herauszufinden, welche Kind-Eltern-Kommunikationen erforderlich sind, damit der Compiler Sie bei der Implementierung dieser Kommunikationen anleiten kann, während diese Kommunikation in NoMap impliziter ist
  • Der Übersetzer-Ansatz scheint eine gute Idee zu sein, um eine externe Komponente wie elm-autocomplete mit eigenen Nachrichten zu versehen
  • Ich fand es schwierig, dem Übersetzermuster zu folgen, wobei es schwieriger war, Fehlermeldungen des Elm-Compilers beim Erstellen zu verstehen
  • Wenn Sie den Standard (Model, Cmd Msg) nicht ändern, können Sie die Fine Elm-Return-Bibliothek verwenden
  • Einige Leute halten es nicht für empfehlenswert, Html.map zu verwenden, um das Erstellen von "Komponenten" zu vermeiden.
  • Wenn Sie diese Ansätze mischen, können Sie viele Vorteile erzielen. Sie können beispielsweise Html.map für Ansichten vermeiden, OutMsg für Aktualisierungen verwenden oder NoMap nur für Nachrichten der obersten Ebene verwenden, wobei OutMsgs weiter unten aufgeführt ist. beim Rendern einer externen übersetzten Komponente

Ressourcen

Ich glaube, dass die Kommunikation zwischen Kindern und Eltern beim Skalieren und Ausführen von SPAs oft wichtiger ist. Aus diesem Grund habe ich viel über das Skalieren von Elm-Apps gelesen:

Prost!