Elm 组件和视图:何时应该使用 `Html msg` 以及何时使用 `Html Msg`

Elm components and views: When we should use `Html msg` and when `Html Msg`

我正在做一个项目,我和我的队友正在讨论哪种方法更好。

在我看来Html msg似乎更通用,所以我认为我们应该尽可能使用它,但我不能给他其他理由。

此外,我来自 React 和 Redux,在我看来,带有签名 Html Msg 的组件就是我们所说的 Smart/Connected ComponentsHtml msg 就像 Dumb Components,我这个假设对吗?

你能告诉我什么时候应该使用每一个吗?

谢谢

将视图函数注释为 returning Html msg 意味着您没有将视图绑定到特定的 Msg 类型。因此它更加灵活,但它也限制了您可以做的事情。

在编写不呈现任何特定于特定 Msg 的代码的共享代码时,使用小写版本很有用。例如,您可以标准化一些布局代码:

pageTitle : String -> Html msg
pageTitle title =
    h1 [ class "page-title" ] [ text title ]

上面的代码可以returnHtml Msg但是这会限制它的共享能力,因为你会把它绑定到一个特定的Msg类型。

现在,如果您正在编写能够生成特定事件的东西并将其绑定到 Msg 构造函数,那么您必须 return Html Msg:

type Msg = RollDice | Rolled Int

diceButton : String -> Html Msg
diceButton label =
    button [ class "fancy-btn", onClick RollDice ] [ text label ]

如果您尝试 return 小写 Html msg,上述定义将无法编译。

如果我们就此打住,我可以看出这与 React 中的智能组件与哑组件想法类似,因为小写版本似乎无法呈现 Html 会触发事件,但这个类比并不成立。假设您想在系统范围内标准化一个按钮,但不想将其绑定到特定的 Msg。您可以通过接受点击时触发的 Msg 参数来使该函数更通用。

fancyButton : String -> msg -> Html msg
fancyButton label msg =
    button [ class "fancy-btn", onClick msg ] [ text label ]

一般来说,如果您正在编写要在内部共享或作为外部包共享的代码,您将通过使用 Html msg 的小写版本提供更大的灵活性。这样做,您仍然可以编写可以触发事件的 Html,就像上面的 fancyButton 示例一样,这只是意味着您将一些责任传递给调用函数,它必须决定什么Msg 传入。

msgMsg确实是根本不同的种类m/M 是大写还是小写很重要!

User-defined 类型

type Msg = RollDice | Rolled Int 正在定义 new 类型,该类型特定于 您的程序 。您可以随意命名(只要以大写字母开头)。事实上,Msg 在这里真的是一个可怕的名字——这些消息是关于什么的?我不知道!

而是:type DiceMsg = RollDice | Rolled Int。啊——现在我明白了,这是一个关于骰子的类型(消息)。知道了! :)

也许你的程序还有:

type Suit = Spades | Hearts | Clubs | Diamonds
type CardMsg = DrawCard | CardDrawn Suit Int

那你就知道有什么区别了:

Html DiceMsg -- 这是一些 html 会产生关于骰子的信息。

Html CardMsg -- 这是一些 html 会产生关于卡片的信息。

适用于 多种 user-defined 类型的代码

现在让我们像 h1 [ class "page-title" ] [ text title ] 一样考虑 html。 html 的 种类 是什么?

你可以看到它根本不产生任何消息,所以称它为Html DiceMsg[=20=真的没有意义].但是,它可以 任何一种 html 组合,对吧?您可以将这样的标题放在 Html DiceMsg 块上方,然后您会得到... 更大的 Html DiceMsg.

所以这就是 Html msg (lower-case m) 的意思:lower-case msg 的意思是“它可以是任何东西,让编译器想办法”。编译器将根据您使用它的上下文来决定 msgDiceMsg 的占位符还是 CardMsg 的占位符。 (在技术术语中,msg 是一个 类型变量 。)

参数化 msg

为了获得最大的灵活性,您可以让函数的调用者控制他们希望您的代码生成哪种消息:

fancyButton : String -> msg -> Html msg
fancyButton label msg =
    button [ class "fancy-btn", onClick msg ] [ text label ]

这里,msg既是又是return类型的参数类型。所以编译器可以计算出像 fancyButton "draw a card" DrawCard 这样的调用会产生 Html CardMsg,而 fancyButton "roll the dice" RollDice 会产生 Html DiceMsg.