Komunikace rodičů a rodičů v Elmu: OutMsg vs Translator vs NoMap Patterns

Když vaše aplikace Elm začne růst, budete ji chtít rozdělit na menší kousky, abyste mohli škálovat. Pokryl jsem to v dalším blogu: Strukturovaný příklad TodoMVC s Elmem.

Část, pokud toto měřítko nakonec zahrnuje potřebu poslat Msg z modulu jeho nadřazenému, jako když navigační tlačítka v určitém pohledu musí poslat zprávu do směrovače nejvyšší úrovně.

Po nějaké době jsem si všiml 3 různých vzorců, jak to zvládnout, a Elm TodoMVC jsem refakturoval na všechny ty odlišné přístupy v tomto úložišti, abyste je mohli porovnat vedle sebe.

Vzor OutMsg

Věřím, že Folkertdev byl první, kterého jsem viděl psát o komunikaci rodič-dítě v Elmu, jeho blogpost tento přístup vysvětluje docela dobře.

Abych to shrnul, v podstatě vrátíš ve své aktualizační funkci extra hodnotu. Takže namísto návratu:

(Model, Cmd Msg)

Vrátíte toto:

(Model, Cmd Msg, OutMsg)

Poté je za jejich zpracování zodpovědná funkce rodičovské aktualizace. Tímto způsobem dítě nemusí vědět nic o svém rodiči, ale rodič musí vědět o jeho OutMsgs dítěte.

Pomocí tohoto přístupu jsem implementoval TodoMVC. Pokud však chcete zkontrolovat měřítko skutečného světa, Richard Feldman provedl příklad elm-spa tímto způsobem.

Dalším příkladem, který používá tento přístup, je elm-datepicker.

Vzor překladatele

Translator Pattern je velmi podobný OutMsg, ale místo toho, aby rodič věděl o typech Msgs dítěte, je to rodič, který prochází, které Msgs budou generovány, prostřednictvím překladače. Alex Lew zde vysvětluje svůj přístup mnohem lépe.

V zásadě máte překladatele, který je záznamem jako je tento:

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

Také jsem implementoval TodoMVC pomocí tohoto přístupu a věřím, že elm-autocomplete je také dobrým příkladem.

Elm-parent-child-update je knihovna, která vám pomůže s aktualizací child-parent, která podle všeho následuje tento vzorec.

Vzor NoMap

To je něco, co jsem si všiml, že dělám. Základní myšlenkou je vyhnout se provádění Cmd.map a Html.map, takže místo toho musí každý mluvit stejným jazykem, jinými slovy, vaše funkce aktualizace a zobrazení budou muset vrátit typ Msg nejvyšší úrovně.

S tímto budete pravděpodobně mít Msgs jako MsgForLogin, MsgForRouter atd., Takže ve svém Pohledu byste udělali něco jako:

tlačítko [onClick (MsgForLogin SignUp)] []

Takto jsem poprvé refaktoroval TodoMVC, vlastně poprvé, když jsem viděl OutMsg, nerozuměl jsem tomu důvodu, protože jsem nemapoval mé zprávy.

Podívejte se na aplikaci Lightning-Talk-app pro větší příklad s tímto přístupem. Zdá se také, že tato aplikace následuje způsob, jak strukturovat aplikace Elm, Kris Jenkins, což tento přístup upřednostňuje, když odděluje typy Msgs v souboru Types.elm.

Knihovna elm-taco trochu používá kombinaci vzorů OutMsg a NoMap tím, že má nejvyšší „taco“, na které můžete posílat zprávy.

Pozorování a srovnání

Při zkoumání a refaktoringu těchto vzorců jsem zaznamenal něco, co může být podle vašich potřeb výhodou nebo nevýhodou:

  • Na NoMap se funkce aktualizace rodiče udržuje stejně jako vaše aplikace, zatímco u OutMsg a Translate může být funkce aktualizace rodiče velmi velká, protože je třeba zpracovat OutMsg každého dítěte (příklad)
  • Na OutMsg and Translate nemusí vnořené moduly importovat nic od vyšších rodičů, což je činí více zapouzdřenými, bylo by například snazší extrahovat a publikovat nějaký sub-modul jako knihovnu, například
  • Aby NoMap fungoval, vaše Msgs by měla žít v samostatném souboru od Update, jinak budete mít smyčku závislosti. To je dobrá příčina, která vás nutí rozdělit věci, ale zároveň je to špatné, pokud chcete mít pro každý modul jeden soubor (Home.elm, Login.elm, Router.elm)
  • V NoMap je snazší posílat zprávy Msgs kdekoli jinde, ale může být těžší sledovat všechny změny stavu, které jsou způsobeny.
  • Jak je měřeno v okamžiku tohoto psaní, pro refaktory TodoMVC má přístup NoMap 546 LOC, OutMsg 561 a Translator 612, pokud vám to záleží
  • Na NoMap budete nakonec muset použít _ catch-all case pro ignorování Msgs z jiných míst, které nechcete zpracovat, takže je méně nápovědy od kompilátoru, nemůže říct, co vám chybí (díky za @mordrax za směřování to na slaboch)
  • V OutMsg a Translator se můžete jen podívat na typy nebo překladače, abyste zjistili, které komunikace mezi rodiči a rodiči jsou potřebné, takže kompilátor vás může implementovat, zatímco v NoMap je tato komunikace implicitnější
  • Zdá se, že překladatelský přístup je dobrý nápad, jak dát svým vlastním zprávám externí komponentu, jako je elm-autocomplete.
  • Zjistil jsem, že překladatelský vzor je obtížné následovat s těžším pochopením chybových zpráv od kompilátoru Elmu při jeho vytváření
  • Pokud neupravíte standard (Model, Cmd Msg), můžete použít knihovnu jemných elm-return
  • Někteří lidé považují Html.map za dobrou praxi, aby se vyhnuli vytváření „komponent“
  • Spojením těchto přístupů můžete získat mnoho výhod, například byste se mohli vyhnout Html.map pro zobrazení, zatímco stále používáte OutMsg pro aktualizace, nebo byste mohli použít NoMap pouze pro nejvyšší úrovně zpráv, s OutMsgs níže, při vykreslování externí komponenty Translated

Zdroje

Věřím, že Child-Parent Communication je často důležitější při škálování a provádění SPA, proto mnoho věcí, které jsem našel, četlo toto reddit vlákno o Scaling Elm Apps:

Na zdraví!