Cmd.map 是将 Elm SPA 拆分为模块的正确方法吗?

Is Cmd.map the right way to split an Elm SPA into modules?

我正在用 Elm 构建一个单页应用程序,但在决定如何在文件中拆分我的代码时遇到困难。

我最终使用每页 1 个模块将其拆分,并让 Main.elm 使用 Cmd.map 和 [=15= 转换每个页面发出的 HtmlCmd ].

我的问题是 Cmd.map 和 Html.map says that 的文档:

This is very rarely useful in well-structured Elm code, so definitely read the section on structure in the guide before reaching for this!

我检查了我所知道的仅有的 2 个大型应用程序:

  1. elm-spa-example 使用 Cmd.map (https://github.com/rtfeldman/elm-spa-example/blob/cb32acd73c3d346d0064e7923049867d8ce67193/src/Main.elm#L279)
  2. 我无法弄清楚 https://github.com/elm/elm-lang.org 处理这个问题。

此外, 的两个答案都建议使用 Cmd.map,无需再三思。

Cmd.map 是在模块中拆分单个页面应用程序的“正确”方法吗?

我认为有时候您只需要做对适合您的事情。我对我编写的应用程序使用了 Cmd.map/Sub.map/Html.map 方法,该应用程序具有 3 个“页面”——初始化、编辑和报告。

我想让这些页面中的每一个都成为自己的模块,因为它们相对复杂,每个页面都有相当数量的消息,这些消息只与每个页面相关,并且更容易在每个页面自己的上下文中独立推理.

缺点是编译器不会阻止您收到给定页面的错误消息,从而导致运行时错误(例如,如果应用程序在 Reporting 页面,正确的行为是什么?对于我的具体实现,我只是将其记录到控制台并继续 - 这对我来说已经足够了(而且它从未发生过);我考虑过的其他选项包括显示一个讨厌的错误页面,表明发生了可怕的事情 - 如果你愿意的话,一个蓝屏死机;或者只是 reset/reinitialize 整个应用程序)。

另一种方法是使用 效果模式 ,如 this discourse post 中广泛描述的那样。

这种方法的核心是:

The extended Effect pattern used in this application consists in definining an Effect custom type that can represent all the effects that init and update functions want to produce.

主要好处:

  1. All the effects are defined in a single Effect module, which acts as an internal API for the whole application that is guaranteed to list every possible effect.
  2. Effects can be inspected and tested, not like Cmd values. This allows to test all the application effects, including simulated HTTP requests.
  3. Effects can represent a modification of top level model data, like the Session 3 when logging in 3, or the current page when an URL change is wanted by a subpage update function.
  4. All the update functions keep a clean and concise Msg -> Model -> ( Model, Effect Msg ) 2 signature.
  5. Because Effect values carry the minimum information required, some parameters like the Browser.Navigation.key are needed only in the effects perform 3 function, which frees the developer from passing them to functions all over the application.
  6. A single NoOp or Ignored String 25 can be used for the whole application.