我们在Elm应用程序中使用太大的Msg进行战斗

根据Elm Architecture,所有应用程序逻辑都集中在一个地方。 这是一种相当简单和方便的方法,但是随着应用程序的增长,您可以看到update功能的长度为700行, Msg具有一百个构造函数,而Model则不适合屏幕。


这样的代码很难学习并且经常维护。 我想演示一种非常简单的技术,它将提高您的应用程序中的抽象级别。


让我们看一个简单的例子。


首先,创建一个只有一个文本字段的小型应用程序。 完整的代码可以在这里找到。


 type alias Model = { name : String } view : Model -> Html Msg view model = div [] [ input [ placeholder "Name", value model.name, onInput ChangeName ] [] ] type Msg = ChangeName String update : Msg -> Model -> Model update msg model = case msg of ChangeName newName -> { model | name = newName } 

该应用程序正在增长,我们添加了姓氏“关于我们自己”和“保存”按钮。 在这里提交。


 type alias Model = { name : String , surname : String , bio : String } view : Model -> Html Msg view model = div [] [ input [ placeholder "Name", value model.name, onInput ChangeName ] [] , br [] [] , input [ placeholder "Surname", value model.surname, onInput ChangeSurname ] [] , br [] [] , textarea [ placeholder "Bio", onInput ChangeBio, value model.bio ] [] , br [] [] , button [ onClick Save ] [ text "Save" ] ] type Msg = ChangeName String | ChangeSurname String | ChangeBio String | Save update : Msg -> Model -> Model update msg model = case msg of ChangeName newName -> { model | name = newName } ChangeSurname newSurname -> { model | surname = newSurname } ChangeBio newBio -> { model | bio = newBio } Save -> ... 

没什么了不起,一切都很好。


但是,当我们决定在页面中添加与现有组件完全不相关的其他组件时,复杂性会急剧增加,即狗的表单。 提交


 type Msg = ChangeName String | ChangeSurname String | ChangeBio String | Save | ChangeDogName String | ChangeBreed String | ChangeDogBio String | SaveDog update : Msg -> Model -> Model update msg model = case msg of ChangeName newName -> { model | name = newName } ChangeSurname newSurname -> { model | surname = newSurname } ChangeBio newBio -> { model | bio = newBio } Save -> ... ChangeDogName newName -> { model | dogName = newName } ChangeBreed newBreed -> { model | breed = newBreed } ChangeDogBio newBio -> { model | dogBio = newBio } SaveDog -> ... 

在此阶段,您已经可以看到Msg包含两个“组”消息。 我的“程序天赋”建议您需要对此类内容进行抽象。 如果再出现5个组件,将会发生什么? 子组件呢? 在此代码中导航几乎是不可能的。


我们可以引入这个附加的抽象层吗? 当然可以


 type Msg = HoomanEvent HoomanMsg | DoggoEvent DoggoMsg type HoomanMsg = ChangeHoomanName String | ChangeHoomanSurname String | ChangeHoomanBio String | SaveHooman type DoggoMsg = ChangeDogName String | ChangeDogBreed String | ChangeDogBio String | SaveDog update : Msg -> Model -> Model update msg model = case msg of HoomanEvent hoomanMsg -> updateHooman hoomanMsg model DoggoEvent doggoMsg -> updateDoggo doggoMsg model updateHooman : HoomanMsg -> Model -> Model updateHooman msg model = case msg of ChangeHoomanName newName -> { model | name = newName } -- Code skipped -- updateDoggo : DoggoMsg -> Model -> Model -- Code skipped -- view : Model -> Html Msg view model = div [] [ h3 [] [ text "Hooman" ] , input [ placeholder "Name", value model.name, onInput (HoomanEvent << ChangeHoomanName) ] [] , -- Code skipped -- , button [ onClick (HoomanEvent SaveHooman) ] [ text "Save" ] , h3 [] [ text "Doggo" ] , input [ placeholder "Name", value model.dogName, onInput (DoggoEvent << ChangeDogName) ] [] , -- Code skipped -- ] 

利用Elm类型系统,我们将消息分为两种类型:人类和犬类。 现在,输入此代码的门槛将更加容易。 一旦某些开发人员需要更改其中一个组件中的某些内容,他将能够通过类型结构立即确定他需要代码的哪些部分。 是否需要为狗信息保留添加逻辑? 查看消息并开始搜索。


想象一下,您的代码是一个巨大的参考。 您将如何搜索您感兴趣的信息? 按目录(消息和模型)。 您可以轻松地浏览目录而无需分为几节和小节吗? 几乎没有


结论


这是一种非常简单的技术,可以在任何地方使用,并且很容易嵌入到现有代码中。 依靠静态类型和我们最喜欢的elm编译器,重构现有应用程序将完全轻松。


仅花费了一个小时的时间(我们在项目中的每个应用程序上花费了不到20分钟的时间),就可以显着提高代码的可读性,并为以后的编写方式设置标准。 不是易于纠正错误的代码是好的,但是禁止错误并设置代码编写方式的示例。


可以将完全相同的技术应用于Model ,突出显示类型中的必要信息。 例如,在我们的示例中,模型只能分为两种类型: HoomanDoggo ,将模型中的字段数减少为两种。


上帝保佑榆木类型系统。


如果您想查看差异,可以在这里找到带有代码的PS存储库

Source: https://habr.com/ru/post/zh-CN455553/


All Articles