Feliz: Minha jornada com Feliz | Uma comparação entre Fable.React e Feliz

Criado em 7 abr. 2020  ·  23Comentários  ·  Fonte: Zaid-Ajaj/Feliz

Olá,

esta edição é para compartilhar minha experiência com Feliz e Feliz.Bulma.

Este feedback é baseado na conversão de Fable Repl de Fulma para Feliz + Feliz.Bulma .

Na primeira seção, farei uma lista de todas as coisas que gostei ou tive problemas ao usar o Feliz. Farei algo semelhante para Fable.React e Fulma porque eles não estão isentos de parte boa e menos boa.

Depois, num segundo momento, tentarei explicar como foi minha experiência com Feliz e Feliz.Bulma. O objetivo é compartilhar com vocês como meu ponto de vista mudou ao longo do tempo e por quê.

Importante

Sei que o assunto que estou analisando é sensível e isso é algo que me assusta. Mas, por favor, lembre-se de manter a seção de comentários positiva.

Tabela de conteúdo

Sistema de categorização

Tentei encontrar uma maneira de organizar meus feedbacks, o clássico "prós vs contras" parecia muito limitado e agressivo para mim.

Em vez disso, usarei símbolos:

  • ✔️ representa algo de que gostei
  • ⚠️ pode representar várias coisas:

    • algo que me causou problemas porque é diferente do que estou acostumada

    • algo que poderia ser melhorado

    • áreas onde o usuário precisa ter cuidado

  • ℹ representa algo que não era óbvio para mim no início, mas não está diretamente relacionado a um problema devido a Feliz

A mesma entrada pode ter vários símbolos :)

Feliz


✔️ Feliz e Fable.React podem ser misturados porque Feliz é uma camada sobre Fable.React


✔️ O código é mais confortável para recuar em comparação com Fable.React porque temos apenas uma única lista. É mais fácil seguir a tabulação.

Clique aqui para mais detalhes

** Fable.React + Fulma **

div [ ]
    [ Hero.hero [ Hero.IsFullHeight ]
        [ Hero.body [ ]
            [ Container.container [ ]
                [ img [ Src "img/fable-ionide.png"
                        Style [ Display DisplayOptions.Block
                                Width "auto"
                                Margin "auto" ] ]
                  br [ ]
                  Heading.h3 [ Heading.Modifiers [ Modifier.TextAlignment (Screen.All, TextAlignment.Centered) ] ]
                    [ str "Fable REPL" ]
                  Heading.p [ Heading.IsSubtitle
                              Heading.Is5
                              Heading.Modifiers [ Modifier.TextAlignment (Screen.All, TextAlignment.Centered) ] ]
                    [ str "is only available on desktop" ] ] ] ] ]
** Feliz + Feliz.Bulma **
Html.div [
    Bulma.hero [
        hero.isFullHeight
        prop.children [
            Bulma.heroBody [
                Bulma.container [
                    Html.img [
                        prop.src "img/fable-ionide.png"
                        prop.style [
                            style.display.block
                            style.width length.auto
                            style.margin length.auto
                        ]
                    ]

                    Html.br [ ]

                    Bulma.title3 [
                        text.hasTextCentered
                        prop.text "Fable REPL"
                    ]

                    Bulma.subtitle5 [
                        text.hasTextCentered
                        prop.text "is only available on desktop"
                    ]
                ]
            ]
        ]
    ]
]


✔️ Graças ao ponto anterior, também é fácil mover o código

feliz_move_code_demo


✔️ API fortemente tipada para as propriedades DOM


✔️ API fortemente tipada para CSS e unidades CSS por meio de ICSSUnits

style.marginLeft (length.em 0.5)
style.width (length.percent (model.PanelSplitRatio * 100.))

✔️ Não polui o contexto, a maioria das coisas estão sob Html.* ou Prop.*


✔️ ⚠️ Feliz oferece a possibilidade de evitar ruídos no código

Clique aqui para mais detalhes

Html.span "Fable REPL"

// instead of

// Feliz verbose version
span [
    str "Fable REPL"
]

// Fable.React

span [ ]
    [ str "Fable REPL" ]

// --------------------

Bulma.title4 "Fable REPL"

// instead of

// Feliz.Bulma verbose version
Bulma.title4 [
    prop.text "Fable REPL"
]

// Fable.React
Headings.h4 [ ]
    [ str "Fable REPL" ]
Como existem várias maneiras de escrever o mesmo código, não podemos simplesmente "ler" o código. Às vezes, precisamos dar um passo para trás para entender o contexto externo. Isso também torna o código menos "consistente" porque nem tudo é escrito da mesma maneira.
Html.tr [
    Html.th "Steps"
    Html.th [
        prop.className "has-text-right"
        prop.text "ms"
    ]
]


⚠️ Não suporta (ainda) SSR


⚠️ Descobrir a sobrecarga de métodos / propriedades não é fácil (talvez seja uma limitação do Ionide, não sei)

Eu precisei:

  • experimente sozinho e verifique o erro do compilador. Por exemplo, testar se prop.text 2.0 é suportado ou não quando eu tinha um float.
  • olhe diretamente para o código-fonte de Feliz

⚠️ Torne mais difícil o uso de callback usando mais de um parâmetro. Em Fable.REPL, tive que forçar a descompressão usando System.Func<_,_,_> .

Clique aqui para mais detalhes

[<Erase>]
type editor =
    /// <summary>Triggered when the Editor has been mounted</summary>
    static member inline editorDidMount (f : System.Func<Monaco.Editor.IStandaloneCodeEditor, Monaco.IExports, unit>) = Interop.mkAttr "editorDidMount" f
O que fornece este código no lado da chamada:
let private registerCompileCommand dispatch =
    System.Func<_,_,_>(
        fun (editor : Monaco.Editor.IStandaloneCodeEditor) (monacoModule : Monaco.IExports) ->
            let triggerCompile () = StartCompile None |> dispatch
            editor.addCommand(monacoModule.KeyMod.Alt ||| int Monaco.KeyCode.Enter, triggerCompile, "") |> ignore
            editor.addCommand(monacoModule.KeyMod.CtrlCmd ||| int Monaco.KeyCode.KEY_S, triggerCompile, "") |> ignore
    )


✔️ ⚠️ Feliz proporciona sobrecargas para facilitar um pouco a vida do usuário, mas isso tem um custo.

Por prop.onChange , como 6 sobrecargas, dependendo do que você deseja ouvir:

  • static member inline onChange (handler: bool -> unit)
  • static member inline onChange (handler: Event -> unit)
  • static member inline onChange (handler: File -> unit)
  • static member inline onChange (handler: File list -> unit)
  • static member inline onChange (handler: string -> unit)
  • static member inline onCheckedChange (handler: bool -> unit)

Isso é bom porque evita que as pessoas escrevam códigos "não" divertidos, mas a contraparte é que você precisa dizer qual tipo de evento deseja explicitamente.

// Feliz
prop.onChange (fun (e : Types.Event) -> e.Value |> ChangeGistToken |> dispatch)

// Fable.React
prop.onChange (fun e -> e.Value |> ChangeGistToken |> dispatch)

✔️ ⚠️ O ecossistema Feliz é quase totalmente seguro para tipos, mas não impede a escrita de código inválido. Ao usar Feliz e uma de suas extensões, como Feliz.Bulma, você pode misturar propriedades facilmente, mas é preciso ter cuidado ao fazê-lo.

Clique aqui para mais detalhes

Este código parece OK do ponto de vista de Feliz e do compilador F #, mas não lhe dará o resultado esperado.

Html.p [
    text.isUppercase
    text.isItalic
    color.hasTextSuccess
    prop.text "Hello Feliz"
]
`text.isUppsercase`,` text.isItalic` e `color.hasTextSuccess` produzem algo como` ClassName "my-css-class`. Mas no React, apenas o último terá efeito, então em nosso caso o código irá gerar:
<p class="has-text-success">Hello Feliz<\p>
ao invés de
<p class="is-uppercase is-italic has-text-success>Hello Feliz</p>
A solução para isso é usar o `++` oferecido por Feliz.Bulma:
open Feliz.Bulma.Operators

Html.p [
    text.isUppercase
    ++ text.isItalic
    ++ color.hasTextSuccess
    prop.text "Hello Feliz"
]
Esta ainda é uma boa possibilidade porque significa que você pode adicionar um comportamento proveniente de outra biblioteca Feliz a um "elemento Feliz" se eles forem compatíveis. As pessoas só precisam ter cuidado com isso.


ℹ A princípio pensei que Feliz não oferecia a sintaxe sugar ev.Value que obtemos ao usar Fable.React, mas não é o caso.

Fable.React.Extension hospeda o açúcar da sintaxe, então podemos apenas abri-lo para não poluir o contexto com todas as funções Fable.React.

open Feliz
open Fable.React.Extensions

ℹ A sintaxe não reproduz a maneira de pensar do HTML. Feliz é mais um açúcar de sintaxe no topo da API React do que HTML. Feliz pensa em termos de propriedades porque até children é uma propriedade.


Feliz.Bulma

✔️ Integra-se bem com Feliz


✔️ Mais fácil de recuar em comparação com Fulma

Clique aqui para mais detalhes

// Fulma
Card.card [ ]
    [ Card.header [ Common.Props [ OnClick (fun _ -> ToggleWidget title |> dispatch ) ] ]
        [ Card.Header.title [ ]
            [ Icon.icon [ Icon.Props [ Style [ MarginRight ".5em" ] ] ]
                [ Fa.i [ icon; Fa.Size Fa.FaLarge ] [] ]
                str title ]
            Card.Header.icon [ ]
            [ Icon.icon [ ] [ Fa.i [ headerIcon ; Fa.Size Fa.FaLarge ] [] ] ] ]
        ofOption content ]

// Feliz.Bulma
Bulma.card [
    Bulma.cardHeader [
        prop.onClick (fun _ -> ToggleWidget title |> dispatch )
        prop.children [
            Bulma.cardHeaderTitle [
                Bulma.icon [
                    prop.style [
                        style.marginRight (length.em 0.5)
                    ]
                    prop.children [
                        Fa.i [ icon; Fa.Size Fa.FaLarge ] []
                    ]
                ]
                Html.text title
            ]

            Bulma.cardHeaderIcon [
                Bulma.icon [
                    Fa.i [ headerIcon ; Fa.Size Fa.FaLarge ] []
                ]
            ]
        ]
    ]

    Html.ofOption content
]


✔️ É fácil identificar Bulma componentes graças ao prefixo Bulma


✔️ ⚠️ As propriedades são acessíveis, mas poluem o contexto button.* , help.* , columns.* .

Em teoria, as pessoas deveriam usar apenas um framework CSS, então eu não acho que eles terão um conflito de propriedades genéricas como button.* , columns.* , etc.


✔️ A hierarquia de componentes parece fácil de entender

Bulma.card > Bulma.cardHeader > Bulma.cardHeaderTitle

Clique aqui por exemplo

Bulma.card [
    Bulma.cardHeader [
        prop.onClick (fun _ -> ToggleWidget title |> dispatch )
        prop.children [
            Bulma.cardHeaderTitle [
                // ...
            ]

            Bulma.cardHeaderIcon [
                // ...
            ]
        ]
    ]
]


⚠️ Mas alguns dos componentes não seguem a mesma convenção

Bulma.passwordInput vez de Bulma.inputPassword

Nesse caso, você não pode explorar facilmente os diferentes tipos de entrada que possui porque eles não começam com o mesmo "prefixo".


✔️ ⚠️ Feliz.Bulma facilita a mistura de comportamentos de componentes.

Clique aqui para mais detalhes

Bulma.button [
    // Properties specific to a button coming from Feliz.Bulma
    button.isOutlined
    // Properties specific to a tooltip coming from Feliz.Bulma
    tooltip.hasTooltipRight
    tooltip.text tooltipText
    // General properties coming from Feliz
    prop.disabled isCompiling
    prop.onClick (fun _ -> dispatch msg)
    prop.children [
        Bulma.icon [
            icon.isLarge
            prop.children faIcon
        ]
    ]
]
Isso é bom porque, como você vê, é fácil adicionar um novo comportamento a um componente existente. Aqui, adicionamos a dica de ferramenta de comportamento a um botão. Mas isso também significa que você pode escrever um código inválido como:
Html.select [
    select.isFullwidth
]
ao invés de
Bulma.select [
    select.isFullwidth
]
Fulma é mais rígido quanto à separação de componentes e não permite que você misture o comportamento, a menos que você passe as classes CSS por meio de adereços `CustomClass`.


✔️ ⚠️ Feliz.Bulma não fornece uma maneira de controlar qual elemento HTML queremos gerar.

Por exemplo, temos apenas Bulma.field que gera um div . No entanto, às vezes você deseja um elemento p como saída.


✔️ ⚠️ Feliz.Bulma é um mapeamento exato sobre Bulma

Isso é bom porque é fino.

Mas também significa:

  • Você precisa conhecer a Bulma bem o suficiente ou abrir a documentação da Bulma
  • Não evita códigos barulhentos

Por exemplo, ao escrever componentes de Bulma.tabs você não é guiado e precisa saber que tabs precisa de ul seguido por li com um a nele.

Clique aqui para ver o código

// Feliz + Feliz.Bulma
Bulma.tabs [
    tabs.isCentered
    tabs.isMedium
    tabs.isToggle
    prop.children [
        Html.ul [
            Html.li [
                if (activeTab = CodeTab.FSharp) then
                    prop.className "is-active"
                prop.onClick (fun _ -> SetCodeTab CodeTab.FSharp |> dispatch)
                prop.children [
                    Html.a [
                        prop.text "F#"
                    ]
                ]
            ]

            Html.li [
                if (activeTab = CodeTab.Html) then
                    prop.className "is-active"
                prop.onClick (fun _ -> SetCodeTab CodeTab.Html |> dispatch)
                prop.children [
                    Html.a [
                        prop.text "HTML"
                    ]
                ]
            ]

            Html.li [
                if (activeTab = CodeTab.Css) then
                    prop.className "is-active"
                prop.onClick (fun _ -> SetCodeTab CodeTab.Css |> dispatch)
                prop.children [
                    Html.a [
                        prop.text "CSS"
                    ]
                ]
            ]
        ]
    ]
]
Fulma permite ao usuário pensar mais em termos de "componentes Bulma".
// Fable.React + Fulma
    Tabs.tabs [ Tabs.IsCentered
                Tabs.Size Size.IsMedium
                Tabs.IsToggle ]
        [ Tabs.tab [ Tabs.Tab.IsActive (activeTab = CodeTab.FSharp)
                     Tabs.Tab.Props [
                        OnClick (fun _ -> SetCodeTab CodeTab.FSharp |> dispatch)
                     ] ]
            [ a [ ] [ str "F#" ] ]
          Tabs.tab [ Tabs.Tab.IsActive (activeTab = CodeTab.Html)
                     Tabs.Tab.Props [
                         OnClick (fun _ -> SetCodeTab CodeTab.Html |> dispatch)
                     ] ]
            [ a [ ] [ str "HTML" ] ]
          Tabs.tab [ Tabs.Tab.IsActive (activeTab = CodeTab.Css)
                     Tabs.Tab.Props [
                         OnClick (fun _ -> SetCodeTab CodeTab.Css |> dispatch)
                     ] ]
            [ a [ ] [ str "CSS" ] ] ]

Observação:

Fulma considera tab como um componente e oferece um invólucro específico para Tabs.Tab.* .

Fulma ainda precisa que você saiba que um elemento a é necessário, mas poderíamos adicioná-lo por padrão (esse é o caso de alguns componentes).


✔️ ⚠️ Feliz.Bulma tem um impacto menor no tamanho do pacote em um projeto pequeno, mas terá um impacto mais significativo quando o tamanho do projeto aumentar.

Clique aqui para mais detalhes

Fulma está usando muitos DUs para modelar classes Bulma. Você pode dar uma olhada em [Common.fs] (https://github.com/Fulma/Fulma/blob/2f99474cd6c793776001d07da009f7211be2f30c/src/Fulma/Common.fs); também cada componente tem suas próprias UDs. Isso requer que Fulma implemente uma chamada de função `parseOptions` que, em termos simples, converte as DUs em classes. Feliz.Bulma adota uma abordagem mais direta, não criando uma DSL no topo das classes Bulma, mas em vez disso produzindo diretamente as classes.

// Fulma
Column.column
    [
        Column.Width (Screen.Desktop, Column.IsHalf)
        Column.Width (Screen.Mobile, Column.IsFull)
    ]
    [
        // ...
    ]

// Feliz
Bulma.column [
    column.isHalfDesktop
    column.isFullMobile
    prop.children [
        // ...
    ]
]
Graças ao uso direto da classe e à manipulação da lista de propriedades, Feliz.Bulma não tem o grande custo de todas as UDs e códigos adicionados no Fulma. No entanto, ele ainda precisa de um passe extra para "ingressar" em todas as classes. Esta parte é feita por meio do [módulo Feliz.Bulma.ElementBuilders.Helpers] (https://github.com/Dzoukr/Feliz.Bulma/blob/3ecbba1579d2a26281f24e6a6664b5d9c5222603/src/Feliz.Bulma/ElementBuilders.fs#L6-L23) but essas funções são embutidas. É por isso que quanto maior for o seu projeto, maior será o impacto no seu código.


⚠️ Não é fácil adicionar um novo suporte de cor.

Em Feliz.Bulma, cada componente contém sua própria implementação de cor, como button.isWarning , help.isWarning , etc.

Então, se você quiser adicionar sua cor, você precisa implementar todos os button.isMyNewColor , help.isMyNewColor

Na Fulma, todos compartilham o mesmo tipo de cor . Veja a documentação


Fable.React

✔️ Segue a estrutura HTML quando você pensa nela como uma lista de propriedades e uma lista de filhos


⚠️ As regras de recuo são difíceis de estabelecer porque precisamos organizar uma lista dupla e, em geral, fazemos muitos casos excepcionais.

Clique aqui para obter uma explicação detalhada

Exemplo:

// When I want to put an empty div
div [ ] [ ]

// When I want to put a div with a single property
div [ ClassName "button" ] [ ]
// or
div [ ClassName "button" ]
    [ ]

// When I want to put a div with several property
div [ ClassName "button"; Id "my-button" ]
    [ ]
// yes but what if one of my property is not that simple?
div
    [
        ClassName "button"
        OnClick (fun _ ->
            // do something
        )
    ]
    [ ]

// It's also possible to have consistent indentation if you do something like that
div [ ] [
    // Children 1
    // Children 2
]

// But if you have non simple property you still have problems, I don't personally don't find it easy to read
// I am not sure if that's how people would write it because I don't use this format personally
div [
    ClassName "button"
    OnClick (fun _ ->
        // do something
    )
] [
    div [ ] [
        str "Children 1"
    ]
]

// and so on
Como você pode ver ao lidar com alguns casos na lista de propriedades, temos várias sintaxes possíveis. Se eu tivesse que estabelecer uma sintaxe consistente em meu projeto, seria algo assim:
div
    [
        // Property 1 ...
        // Property 2 ...
        // Property 3 ...
    ]
    [
        div
            [
                // Property 1 ...
                // Property 2 ...
                // Property 3 ...
            ]
            [
                // Children 1 ...
                // Children 2 ...
                // Children 3 ...
            ]
        div
            [
                // Property 1 ...
                // Property 2 ...
                // Property 3 ...
            ]
            [
                // Children 1 ...
                // Children 2 ...
                // Children 3 ...
            ]
    ]

// So for an empty element
div
    [

    ]
    [

    ]

// Awesome... 🙄
Para comparação em Feliz, eu faço:
// Empty div
Html.div [ ]

// Non empty div
Htmldiv [
    // Property 1
    // Property 2
    // Property 3
    prop.children [
        Html.div [
            // Property 1
            // Property 2
            // Property 3
        ]

        Html.div [
            // Property 1
            // Property 2
            // Property 3
            // Complexe property
            OnClick (fun _ ->
                // Do something
            )
        ]
    ]
]
Portanto, agora tenho apenas 2 casos e poderia até escrever a versão vazia em várias linhas sem muito ruído, se realmente quiser uma única maneira de estruturar o código de Feliz.


⚠️ A maior parte da API não é digitada

type HTMLAttr =
    | DefaultChecked of bool
    | DefaultValue of obj
    | Accept of string
    | AcceptCharset of string
    | AccessKey of string
    | Action of string
    | AllowFullScreen of bool
    | AllowTransparency of bool

✔️ ⚠️ Parte da API é digitada, mas não toda, o que significa que o código não é consistente.


✔️ Suporta SSR


Fulma

✔️ API de tipo seguro

Modifier.TextAlignment (Screen.All, TextAlignment.Centered)

Button.button [ Button.Color IsWhite ]
    [ str "White" ]

✔️ Fulma API é fácil de explorar via intellisense quando você entende como está estruturada


✔️ Fulma força você a pensar em termos de componentes


✔️ Facilite a extensão da cor suportada graças a IsCustomColor

// All you need to add `custom-purple` support to all your components is this line
let isCustomPurple = IsCustomColor "custom-purple"

✔️ ⚠️ Acho que tem uma boa documentação com exemplos para todos os componentes, mas nem todos contêm o mesmo nível de informação


✔️ ⚠️ O impacto do Fulma no tamanho do seu pacote é "estável". Este ponto foi descrito na seção Feliz.Bulma.


⚠️ As pessoas não encontram facilmente "ajudantes especiais".

  • CustomClass
  • Adereços
  • Modificadores

⚠️ O código Fulma adiciona ruído ao seu código por causa do DSL fortemente tipado

Clique aqui para um exemplo

// Fulma
Heading.p [ Heading.IsSubtitle
            Heading.Is5
            Heading.Modifiers [ Modifier.TextAlignment (Screen.All, TextAlignment.Centered) ] ]
    [ str "is only available on desktop" ]

// Feliz.Bulma
Bulma.subtitle5 [
    text.hasTextCentered
    prop.text "is only available on desktop"
]


⚠️ O código Fulma não é tão fácil de formatar corretamente porque tem muitas listas aninhadas quando você combina Props , Modifiers


Tenho trabalhado nesta análise por um período de 2 a 3 semanas e ainda posso continuar. Mas tenho que terminar porque está demorando muito e acho que agora tenho uma visão melhor da situação.

Tudo isso para dizer que poderia haver mais pontos bons ou problemas para listar, especialmente sobre Fable.React e Fulma, mas essa também é a parte mais difícil para mim. Eu os tenho usado e projetado por mais de 3 anos, então é difícil ter uma visão objetiva sobre eles.

A conversão foi difícil?

A conversão de Fable.React para Feliz foi fácil de fazer. Feliz sendo compatível com o Fable.React pode-se fazer a conversão de forma progressiva arquivo por arquivo, componente por componente, etc.

Para ter certeza de que converti tudo, removi todas as instruções open Fable.React e open Fable.React.Props . Também removi Fulma como uma dependência.

Claro, eu tinha esquecido alguns arquivos, então consertei os erros do compilador e então estava funcionando.

Você pode ver a solicitação de mesclagem que hospeda a conversão aqui .


Minha jornada com feliz

Quando descobri Feliz no início, fiquei na defensiva porque não estava a seguir o "caminho HTML / JSX" e é um projecto alternativo para coisas em que investi muito esforço.

Aí me lembrei que é assim que funciona a inovação e é isso que nos permite crescer. Por exemplo, graças a isso temos Elmish hoje.

Outra coisa que me lembrei é que JSX não é a "API React real", na verdade a API React é React.createElement . JSX é um açúcar de sintaxe em cima dele, a mesma coisa vale para Fable.React e Feliz. Insisto nisso porque para mim ajudou-me a seguir em frente e a ficar curioso.

Para testar Feliz, decidi usar um projeto de médio porte que é o Fable REPL. O Fable REPL não é um aplicativo complexo, mas usa muitos recursos do Fable e do React.

Uma lista não exaustiva de coisas que testei graças a esta decisão:

  • Feliz
  • Feliz.Bulma
  • Quão bem Feliz e Fable.React são compatíveis
  • Como é fácil fazer a transição de Fable.React para Feliz
  • Utilização de um componente funcional com chamada nativa ao DOM de Feliz
  • Como escrever uma ligação e uma interoperabilidade com um componente personalizado não trivial escrito em JavaScript à maneira do Feliz
  • A transição de Fulma para Feliz.Bulma para ver o que seria um equivalente de Fulma no ecossistema de Feliz

Ao iniciar a conversão fiquei muito satisfeito com a facilidade de formatar o código. Acho que a convenção para escrever uma visão baseada em Feliz poderia ser muito fácil de seguir. Quanto mais meus aplicativos crescem, mais considero a formatação de código importante.

Então, no meio, comecei a ficar irritado com alguma coisa, mas não conseguia encontrar o quê.

Terminei de converter o Fable REPL em Feliz e não tinha certeza se gostava ou não da minha experiência com ele.

Só agora, que tento concluir minha análise, é que entendo o que me impede. Não está muito relacionado com Feliz, mas mais com Feliz.Bulma.

É importante lembrar que há mais de 2,5 anos venho trabalhando no Fulma e melhorando-o. Fulma é um dos meus maiores projetos. Escrever código com ele é ótimo, especialmente porque tudo é digitado e força você a pensar em termos de componentes.

Feliz.Bulma tem uma abordagem diferente e é mais jovem do que Fulma; tem apenas 5 meses.

Com tudo isso dito, acho que agora é hora de chegar à minha conclusão :)

Conclusão

Acho que a maior vantagem do Feliz é o estilo de lista única. Isso é algo que mencionei muito, mas para mim ser capaz de formatar facilmente meu código e não ter que pensar por onde devo começar a próxima linha é uma grande vantagem.

Em segundo lugar, a API de segurança de tipo é obrigatória. É mais detalhado do que o equivalente em CSS, mas acho que é um bom recurso. Não fiz nada de especial com ele, então não posso dizer se tudo é compatível, mas se não for o caso, podemos sempre fazer um PR para consertar isso :).

Feliz.Bulma mostra um caminho promissor para estender Feliz, mas ainda não está maduro o suficiente para ser um equivalente direto de Fulma. Isso é algo que eu quero explorar mais com @Dzoukr e espero que possamos encontrar um meio-termo entre a simplicidade de Feliz.Bulma e a completude, mas verbosidade de Fulma.

3 anos atrás, em 8 de março de 2017 , Tomas, Eugene e eu decidimos fundir o Fable.Arch com o Elmish. Isso nos permitiu, como comunidade, construir o incrível ecossistema que temos hoje.

Acho que estamos vivendo um período semelhante com Feliz e Fable.React. Acho que Feliz vai ganhar mais força do que Fable.React
mas ambos coexistirão. No final, ambos funcionam bem juntos, o que significa que as pessoas podem escolher aquele que preferem usar.

Obrigado por ler minha análise e terei o maior prazer em discuti-la com você na seção de comentários :)

awesome blog

Comentários muito úteis

Olá @MangelMaxime ,

Em primeiro lugar, quero agradecer por dedicar seu tempo e esforço não apenas para experimentar Feliz em um projeto, mas também por escrever uma análise tão extensa. Tenho certeza de que muitos usuários do Fable o acharão muito útil e informativo.

Eu realmente aprecio esse tipo de feedback e abertura e acredito que é crucial para tornar esta biblioteca o melhor que pode ser junto com seu ecossistema: ore:

Falarei sobre o Feliz.Bulma daqui a pouco porque acho que é meio especial no ecossistema, primeiro vou responder a alguns problemas que você tem que já podem ser consertados sem mudanças na biblioteca.

Sobrecargas de onChange

você precisa dizer qual tipo de evento deseja explicitamente.

Na verdade, você não. O tipo deve ser inferido de seu tipo de mensagem da seguinte maneira:

prop.onChange (ChangeGistToken >> dispatch)

Dependendo do tipo de ChangeGistToken , a sobrecarga correta será inferida. Quando o caso DU for ChangeGistToken of string , a sobrecarga de string -> unit será escolhida. Mas também pode ser FileSelected of File quando o tipo de entrada é file , caso em que a sobrecarga File -> unit será inferida e assim por diante. Você não precisa do açúcar sintático de ev.Value de Fable.React

prop.onCheckedChange será removido porque onChange lida com o caso booleano

Retornos de chamada com vários argumentos

@Shmew já tocou no assunto e, de fato, essa é a maneira de resolver esses tipos de propriedades. É fundamental entender que, ao implementar extensões Feliz, você pode fazer transformações antes de definir a propriedade:

type myExtension = 
    static member inline extenstionProperty value = 
        // transform value here
        let transformedValue = doWeirdStuffWith value
        Interop.mkAttr "extenstionProperty" transformedValue

Seguindo esta lógica, você pode converter 'A -> 'B -> unit em Func<'A, 'B, unit> internamente:

[<Erase>]
type editor =
    /// <summary>Triggered when the Editor has been mounted</summary>
    static member inline editorDidMount (f : Monaco.Editor.IStandaloneCodeEditor -> Monaco.IExports -> unit) = 
        let transformedFunction = System.Func<Monaco.Editor.IStandaloneCodeEditor, Monaco.IExports, unit>(fun editor exported -> f editor exported)
        Interop.mkAttr "editorDidMount" transformedFunction

Esta é uma versão mais explícita do que a fornecida por @Shmew (como você sabe com ligações, você sempre tem que brincar um pouco com elas para acertar). Um exemplo prático disso está aqui

Segurança de tipo no ecossistema Feliz

Como @Shmew mencionou acima: uma extensão Feliz adequada não permite que você escreva código que se comporte incorretamente e restringir os tipos de propriedades permite que você implemente isso. Você pode construir propriedades que retornam IReactProperty como Feliz.Recharts, que permite a compatibilidade com as propriedades existentes em prop ou pode implementar um tipo mais especializado ISpecialiedProperty para oferecer suporte apenas um subconjunto das propriedades enquanto duplica os nomes de algumas propriedades de IReactProperty para garantir a compatibilidade com versões anteriores:

type felizExtension = 
    static member inline specializedProp (value: string) : ISpecializedProperty = unbox (prop.text value) 

É o que venho fazendo com Feliz.AntDesign, por exemplo aqui onde estou duplicando algumas propriedades para disponibilizar a partir do ponto de entrada button (Posteriormente, poderia tornar as propriedades mais especializadas com tipos restritos, mas no início , backward-compat é onde você começa). Eu acredito que Feliz.MaterialUI faz coisas semelhantes para estender IReactProperty ao invés de propriedades especializadas. Isso traz o caso de Feliz.Bulma.

O caso de Feliz.Bulma

Antes de Feliz.Bulma ser implementado, eu tinha em mente que não há necessidade de construir extensões Feliz para frameworks somente CSS. Feliz já torna muito fácil compor vários nomes de classes e trabalhar com eles condicionalmente. Agora, com yield implícito em F # é ainda mais fácil. Combine isso com TypedCssClasses e você removerá a necessidade de implementar qualquer estrutura somente CSS. Custos zero de implementação e manutenção. Basta usar a folha de estilo digitada e usar a documentação já muito boa do próprio projeto, em vez de ter que construir você mesmo um site de documentação.

No entanto, depois de ver a implementação do Feliz.Bulma por @Dzoukr , fez sentido: você poderia simplificar muito o código introduzindo "pontos de entrada" específicos para os componentes CSS de um módulo do Bulma para obter Bulma.button , Bulma.card etc. Estes são realmente agradáveis ​​de usar. Quanto aos modificadores, acho que a abordagem de Roman é fácil de seguir e trabalhar, mas, como você disse, às vezes pode dar resultados interessantes. Certamente, há mais a melhorar nesse departamento e, em qualquer caso, acho que Roman fez um ótimo trabalho ao seguir a visão de Feliz para construir algo fácil de aprender e usar.

Gestão de embalagens de Feliz

Eu só quero apontar uma grande diferença nos atuais Fable.React e Feliz : o controle de versão e gerenciamento de pacotes. As versões do Feliz são todas as suas bibliotecas do ecossistema são compatíveis com Femto, o que elimina o fardo dos usuários de ter que instalar manualmente os pacotes npm necessários ou saber com qual versão eles são compatíveis. Isso é diferente de muitas, senão todas as bibliotecas Fable.React derivadas e até mesmo Fable.React si, onde esse não é o caso.

Planos futuros

  • Escrever (ainda mais) documentação sobre como construir extensões estilo Feliz e o que deve aderir.
  • Construindo um analisador de Feliz para o próprio Feliz, mas possivelmente para seu ecossistema

Todos 23 comentários

Ótima análise.

Para mim, pessoalmente, embora a formatação de lista dupla seja uma dor às vezes, sinto que olhando para o código Fable.React consigo ver melhor a estrutura do html subjacente. É div [.., não Html.div [.... (etc). Não é grande coisa, mas meu cérebro parece ter uma análise mais fácil de Fable.React.

Ótimo, escreva @MangelMaxime , você fez muitos pontos positivos.

Pelo que reuni, a principal conclusão disso são os problemas de como a biblioteca é estendida e os problemas que ocorrem quando não é feito de maneira ideal.

Eu posso definitivamente concordar que escrever bibliotecas para Feliz coloca muito mais trabalho no desenvolvedor, mas na minha opinião vale a pena o esforço quando a API resultante é tão boa de usar.

Escrevi um bom número de extensões para Feliz então acho que posso fornecer alguns insights sobre como mitigar muitos desses problemas:

Sobrecargas

Eu uso o Visual Studio e não tenho problemas para encontrar quais sobrecargas estão disponíveis para mim, talvez isso seja algo que possa ser melhorado no final do ionido?

Poderíamos apenas fazer prop.text ser uma única função inline que requer o membro toString e aceitar qualquer tipo?

Chamadas de retorno:

Ao invés de:

[<Erase>]
type editor =
    /// <summary>Triggered when the Editor has been mounted</summary>
    static member inline editorDidMount (f : System.Func<Monaco.Editor.IStandaloneCodeEditor, Monaco.IExports, unit>) = Interop.mkAttr "editorDidMount" f

Escrever:

[<Erase>]
type editor =
    /// <summary>Triggered when the Editor has been mounted</summary>
    static member inline editorDidMount (f : Monaco.Editor.IStandaloneCodeEditor -> Monaco.IExports -> unit) = Interop.mkAttr "editorDidMount" (Func<_,_,_> f)

Você pode então ligar para a propriedade como seria de esperar do lado do cliente.

Escrevendo código inválido

Eu não vi ninguém aplicar isso tanto quanto eu fiz em algumas de minhas bibliotecas, e pode não ser totalmente aplicável ao estender Html . A maneira de evitar isso é adicionando tipos de interface adicionais às propriedades que você define, em minha maior Feliz library Feliz.Plotly Eu a tenho configurado de forma que você obterá um erro do compilador se você tente usar uma propriedade que não seja válida. A única desvantagem é que é um pouco trabalhoso para o desenvolvedor da biblioteca.


Clique para expandir

[<AutoOpen;EditorBrowsable(EditorBrowsableState.Never)>]
module Types =
  type IPlotProperty = interface end
  type IModeBarButtonsProperty = interface end
  type IAaxisProperty = interface end
  type IAggregateProperty = interface end
  type IAggregationProperty = interface end
  type IAggregationsProperty = interface end
  type IAngularaxisProperty = interface end
  type IAnimationProperty = interface end
  type IAnnotationProperty = interface end
  type IAnnotationsProperty = interface end
  type IAreaProperty = interface end
  type IAspectratioProperty = interface end
  type IAxisProperty = interface end
  type IBarProperty = interface end
  type IBarpolarProperty = interface end
  type IBaxisProperty = interface end
  type IBorderProperty = interface end
  type IBoxProperty = interface end
  type IButtonProperty = interface end
  type ICameraProperty = interface end
  type ICandlestickProperty = interface end
  type ICapsProperty = interface end
  type ICarpetProperty = interface end
  type ICaxisProperty = interface end
  type ICellsProperty = interface end
  type ICenterProperty = interface end
  type IChoroplethProperty = interface end
  type IChoroplethmapboxProperty = interface end
  type ICircleProperty = interface end
  type IColoraxisProperty = interface end
  type IColorbarProperty = interface end
  type IColorscaleProperty = interface end
  type IColorscalesProperty = interface end
  type IConcentrationscalesProperty = interface end
  type IConeProperty = interface end
  type IConfigProperty = interface end
  type IConnectorProperty = interface end
  type IContourProperty = interface end
  type IContourcarpetProperty = interface end
  type IContoursProperty = interface end
  type ICumulativeProperty = interface end
  type ICurrentvalueProperty = interface end
  type IDecreasingProperty = interface end
  type IDeltaProperty = interface end
  type IDensitymapboxProperty = interface end
  type IDiagonalProperty = interface end
  type IDimensionProperty = interface end
  type IDimensionsProperty = interface end
  type IDomainProperty = interface end
  type IEditsProperty = interface end
  type IErrorXProperty = interface end
  type IErrorYProperty = interface end
  type IErrorZProperty = interface end
  type IEyeProperty = interface end
  type IFillProperty = interface end
  type IFilterProperty = interface end
  type IFontProperty = interface end
  type IFrameProperty = interface end
  type IFramesEntryProperty = interface end
  type IFramesProperty = interface end
  type IFunnelProperty = interface end
  type IFunnelareaProperty = interface end
  type IGaugeProperty = interface end
  type IGeoProperty = interface end
  type IGradientProperty = interface end
  type IGridProperty = interface end
  type IGroupbyProperty = interface end
  type IHeaderProperty = interface end
  type IHeatmapProperty = interface end
  type IHeatmapglProperty = interface end
  type IHistogram2dProperty = interface end
  type IHistogram2dcontourProperty = interface end
  type IHistogramProperty = interface end
  type IHoverlabelProperty = interface end
  type IImageProperty = interface end
  type IImagesProperty = interface end
  type IIncreasingProperty = interface end
  type IIndicatorProperty = interface end
  type IInsidetextfontProperty = interface end
  type IIsosurfaceProperty = interface end
  type ILabelfontProperty = interface end
  type ILataxisProperty = interface end
  type ILayerProperty = interface end
  type ILayersProperty = interface end
  type ILayoutProperty = interface end
  type ILeafProperty = interface end
  type ILegendProperty = interface end
  type ILightingProperty = interface end
  type ILightpositionProperty = interface end
  type ILineProperty = interface end
  type ILinkProperty = interface end
  type ILonaxisProperty = interface end
  type IMapboxProperty = interface end
  type IMarginProperty = interface end
  type IMarkerProperty = interface end
  type IMeanlineProperty = interface end
  type IMesh3dProperty = interface end
  type IModebarProperty = interface end
  type INodeProperty = interface end
  type INumberProperty = interface end
  type IOhlcProperty = interface end
  type IOutsidetextfontProperty = interface end
  type IPadProperty = interface end
  type IParcatsProperty = interface end
  type IParcoordsProperty = interface end
  type IPathbarProperty = interface end
  type IPieProperty = interface end
  type IPointcloudProperty = interface end
  type IPolarProperty = interface end
  type IProjectProperty = interface end
  type IProjectionProperty = interface end
  type IRadialaxisProperty = interface end
  type IRangebreakProperty = interface end
  type IRangebreaksProperty = interface end
  type IRangefontProperty = interface end
  type IRangeselectorProperty = interface end
  type IRangesliderProperty = interface end
  type IRotationProperty = interface end
  type ISankeyProperty = interface end
  type IScatter3dProperty = interface end
  type IScatterProperty = interface end
  type IScattercarpetProperty = interface end
  type IScattergeoProperty = interface end
  type IScatterglProperty = interface end
  type IScattermapboxProperty = interface end
  type IScatterpolarProperty = interface end
  type IScatterpolarglProperty = interface end
  type IScatterternaryProperty = interface end
  type ISceneProperty = interface end
  type ISelectedProperty = interface end
  type IShapeProperty = interface end
  type IShapesProperty = interface end
  type ISlicesProperty = interface end
  type ISliderProperty = interface end
  type ISlidersProperty = interface end
  type ISortProperty = interface end
  type ISpaceframeProperty = interface end
  type ISplomProperty = interface end
  type IStartsProperty = interface end
  type IStepProperty = interface end
  type IStepsProperty = interface end
  type IStreamProperty = interface end
  type IStreamtubeProperty = interface end
  type IStyleProperty = interface end
  type IStylesProperty = interface end
  type ISunburstProperty = interface end
  type ISurfaceProperty = interface end
  type ISymbolProperty = interface end
  type ITableProperty = interface end
  type ITernaryProperty = interface end
  type ITextfontProperty = interface end
  type IThresholdProperty = interface end
  type ITickfontProperty = interface end
  type ITickformatstopProperty = interface end
  type ITickformatstopsProperty = interface end
  type ITilingProperty = interface end
  type ITitleProperty = interface end
  type ITotalsProperty = interface end
  type ITracesProperty = interface end
  type ITransformsProperty = interface end
  type ITransitionProperty = interface end
  type ITreemapProperty = interface end
  type IUniformtextProperty = interface end
  type IUnselectedProperty = interface end
  type IUpProperty = interface end
  type IUpdatemenuProperty = interface end
  type IUpdatemenusProperty = interface end
  type IViolinProperty = interface end
  type IVolumeProperty = interface end
  type IWaterfallProperty = interface end
  type IXProperty = interface end
  type IXaxisProperty = interface end
  type IXbinsProperty = interface end
  type IYProperty = interface end
  type IYaxisProperty = interface end
  type IYbinsProperty = interface end
  type IZProperty = interface end
  type IZaxisProperty = interface end
  type IButtonsProperty = inherit IModeBarButtonsProperty
  type IMeasureProperty = interface end
  type ITemplateProperty = interface end

De maneira semelhante, você também pode implementar coisas no código da biblioteca para combinar as propriedades "automagicamente", se desejar. Não tenho certeza de como isso funciona ao usar coisas fora da sua própria biblioteca. O truque para isso é fazer com que esses itens sejam de um tipo diferente de apenas IReactProperty . Por exemplo, em Feliz.Plotly eu faço muitas coisas onde, dependendo das configurações fornecidas, irei modificar / coletar / alterar a entrada do usuário para traduzi-la adequadamente para o que o código real precisa se parecer. É muito bom em alguns aspectos remover coisas que estão sempre presentes.

Conclusão

Acho que grande parte da dor que você teve poderia ser resolvida com uma documentação melhor sobre como navegar escrevendo suas próprias bibliotecas do jeito "Feliz". Talvez @ Zaid-Ajaj e eu possamos trabalhar nisso no futuro.

Obrigado @ l3m

Para mim, pessoalmente, embora a formatação de lista dupla seja uma dor às vezes, sinto que olhando para o código Fable.React consigo ver melhor a estrutura do html subjacente. É div [.., não Html.div [.... (etc). Não é grande coisa, mas meu cérebro parece ter uma análise mais fácil de Fable.React.

Na verdade, mudar a forma de ler o código não é fácil e é por isso que mencionei que não devemos vê-lo como HTML no meu caso, ele me ajudou a fazer a mudança.

Suponho que com F # 5 e a capacidade de "abrir classes estáticas", Feliz será capaz de suportar as sintaxes Html.div e div dependendo se você abre ou não a classe Html.

Obrigado @Shmew pelo comentário e na verdade esqueci que existem vários tipos de "bibliotecas" para o Feliz.

Sobre a "Gravação de código inválido" sim, mas em algumas bibliotecas, queremos "estender" o tipo IReactProperty para evitar a situação Fulma em que você precisa passar propriedades padrão por meio de um auxiliar especial como Props . A razão é que os "componentes" Bulma são apenas uma especialização sobre os elementos DOM.

Eles são apenas código que injeta uma classe por padrão no elemento HTML.

Bulma.button gera <div class="button">

No caso de uma biblioteca que funciona com componentes "reais" (desculpe, não sei como nomeá-la de outra forma), sua solução é de fato a certa. Por exemplo, esta é a abordagem feita por Fable.ReactLeaflet, onde estou definindo as propriedades específicas para cada componente. E, portanto, os componentes não precisam oferecer suporte a todas as propriedades HTML padrão.

Sobre sua solução para "Callbacks" não funcionou para mim. Mas a função passada por Fable tinha argumentos curry e, a menos que eu quisesse escrever algo como myFunction(arg1)(arg2) , eu tinha que forçar a versão não urgente via System.Func .

Se você souber como fazer isso funcionar, ficarei feliz em ver um PR enviado para corrigi-lo em https://github.com/fable-compiler/repl/pull/108/files

Olá @MangelMaxime ,

Em primeiro lugar, quero agradecer por dedicar seu tempo e esforço não apenas para experimentar Feliz em um projeto, mas também por escrever uma análise tão extensa. Tenho certeza de que muitos usuários do Fable o acharão muito útil e informativo.

Eu realmente aprecio esse tipo de feedback e abertura e acredito que é crucial para tornar esta biblioteca o melhor que pode ser junto com seu ecossistema: ore:

Falarei sobre o Feliz.Bulma daqui a pouco porque acho que é meio especial no ecossistema, primeiro vou responder a alguns problemas que você tem que já podem ser consertados sem mudanças na biblioteca.

Sobrecargas de onChange

você precisa dizer qual tipo de evento deseja explicitamente.

Na verdade, você não. O tipo deve ser inferido de seu tipo de mensagem da seguinte maneira:

prop.onChange (ChangeGistToken >> dispatch)

Dependendo do tipo de ChangeGistToken , a sobrecarga correta será inferida. Quando o caso DU for ChangeGistToken of string , a sobrecarga de string -> unit será escolhida. Mas também pode ser FileSelected of File quando o tipo de entrada é file , caso em que a sobrecarga File -> unit será inferida e assim por diante. Você não precisa do açúcar sintático de ev.Value de Fable.React

prop.onCheckedChange será removido porque onChange lida com o caso booleano

Retornos de chamada com vários argumentos

@Shmew já tocou no assunto e, de fato, essa é a maneira de resolver esses tipos de propriedades. É fundamental entender que, ao implementar extensões Feliz, você pode fazer transformações antes de definir a propriedade:

type myExtension = 
    static member inline extenstionProperty value = 
        // transform value here
        let transformedValue = doWeirdStuffWith value
        Interop.mkAttr "extenstionProperty" transformedValue

Seguindo esta lógica, você pode converter 'A -> 'B -> unit em Func<'A, 'B, unit> internamente:

[<Erase>]
type editor =
    /// <summary>Triggered when the Editor has been mounted</summary>
    static member inline editorDidMount (f : Monaco.Editor.IStandaloneCodeEditor -> Monaco.IExports -> unit) = 
        let transformedFunction = System.Func<Monaco.Editor.IStandaloneCodeEditor, Monaco.IExports, unit>(fun editor exported -> f editor exported)
        Interop.mkAttr "editorDidMount" transformedFunction

Esta é uma versão mais explícita do que a fornecida por @Shmew (como você sabe com ligações, você sempre tem que brincar um pouco com elas para acertar). Um exemplo prático disso está aqui

Segurança de tipo no ecossistema Feliz

Como @Shmew mencionou acima: uma extensão Feliz adequada não permite que você escreva código que se comporte incorretamente e restringir os tipos de propriedades permite que você implemente isso. Você pode construir propriedades que retornam IReactProperty como Feliz.Recharts, que permite a compatibilidade com as propriedades existentes em prop ou pode implementar um tipo mais especializado ISpecialiedProperty para oferecer suporte apenas um subconjunto das propriedades enquanto duplica os nomes de algumas propriedades de IReactProperty para garantir a compatibilidade com versões anteriores:

type felizExtension = 
    static member inline specializedProp (value: string) : ISpecializedProperty = unbox (prop.text value) 

É o que venho fazendo com Feliz.AntDesign, por exemplo aqui onde estou duplicando algumas propriedades para disponibilizar a partir do ponto de entrada button (Posteriormente, poderia tornar as propriedades mais especializadas com tipos restritos, mas no início , backward-compat é onde você começa). Eu acredito que Feliz.MaterialUI faz coisas semelhantes para estender IReactProperty ao invés de propriedades especializadas. Isso traz o caso de Feliz.Bulma.

O caso de Feliz.Bulma

Antes de Feliz.Bulma ser implementado, eu tinha em mente que não há necessidade de construir extensões Feliz para frameworks somente CSS. Feliz já torna muito fácil compor vários nomes de classes e trabalhar com eles condicionalmente. Agora, com yield implícito em F # é ainda mais fácil. Combine isso com TypedCssClasses e você removerá a necessidade de implementar qualquer estrutura somente CSS. Custos zero de implementação e manutenção. Basta usar a folha de estilo digitada e usar a documentação já muito boa do próprio projeto, em vez de ter que construir você mesmo um site de documentação.

No entanto, depois de ver a implementação do Feliz.Bulma por @Dzoukr , fez sentido: você poderia simplificar muito o código introduzindo "pontos de entrada" específicos para os componentes CSS de um módulo do Bulma para obter Bulma.button , Bulma.card etc. Estes são realmente agradáveis ​​de usar. Quanto aos modificadores, acho que a abordagem de Roman é fácil de seguir e trabalhar, mas, como você disse, às vezes pode dar resultados interessantes. Certamente, há mais a melhorar nesse departamento e, em qualquer caso, acho que Roman fez um ótimo trabalho ao seguir a visão de Feliz para construir algo fácil de aprender e usar.

Gestão de embalagens de Feliz

Eu só quero apontar uma grande diferença nos atuais Fable.React e Feliz : o controle de versão e gerenciamento de pacotes. As versões do Feliz são todas as suas bibliotecas do ecossistema são compatíveis com Femto, o que elimina o fardo dos usuários de ter que instalar manualmente os pacotes npm necessários ou saber com qual versão eles são compatíveis. Isso é diferente de muitas, senão todas as bibliotecas Fable.React derivadas e até mesmo Fable.React si, onde esse não é o caso.

Planos futuros

  • Escrever (ainda mais) documentação sobre como construir extensões estilo Feliz e o que deve aderir.
  • Construindo um analisador de Feliz para o próprio Feliz, mas possivelmente para seu ecossistema

Na verdade, você não. O tipo deve ser inferido de seu tipo de mensagem da seguinte maneira:

prop.onChange (ChangeGistToken >> dispatch)

Na verdade, não pensei sobre esse uso.

Retornos de chamada com vários argumentos

@Shmew já tocou no assunto e, de fato, essa é a maneira de resolver esses tipos de propriedades.

Desculpe, não vi o final do snippet fornecido por @Shmew. Acabei de ver a declaração de assinatura e pensei que não, não funciona.

Vocês estão certos, podemos fazer a transformação antes de passar o valor para o imóvel.

O caso de Feliz.Bulma

Sim, TypedCssClasses está ok, mas em comparação com Fulma ou Feliz.Bulma, não pode fornecer o mesmo nível de recursos. Porque nem tudo está contido no arquivo CSS.

Outra coisa é que no modo dev, nem sempre temos o arquivo gerado no disco e não tenho certeza se TypedCssClasses suporta este cenário.

Acho que Roman fez um ótimo trabalho ao seguir a visão de Feliz para construir algo fácil de aprender e usar.

Eu concordo, só acho que precisamos adicionar um pouco do trabalho feito na Fulma para torná-lo semelhante. Como eu disse, precisamos encontrar o meio-termo correto entre os dois.

Os recursos ausentes que tenho em mente agora permitem controlar o elemento de saída e tornar mais fácil estender o conjunto de cores com suporte. Não acho que isso vá adicionar muita complexidade.

@ Zaid-Ajaj, obrigado pelo feedback e especialmente por me indicar a direção certa sobre como usar o potencial do Feliz.
Admito que realmente não li a documentação do Feliz (isso é ruim, eu sei), então talvez eles estejam documentados nela. Se não, acho que sabemos mais algumas coisas a serem adicionadas :)

Obrigado @MangelMaxime - essa é uma ótima comparação. Você não mencionou o Fantomas em nenhum lugar para a formatação automática do código - você teve a chance de trabalhar com o Fantomas com o Fable.React ou o Feliz?
Em um dos meus projetos Fable onde uso o Fantomas, ativei a formatação automática ao salvar o arquivo em Ionide e devo dizer que é realmente um impulsionador da produtividade - não há necessidade de se preocupar com a formatação manual!
Eu realmente acho que Fantomas está ganhando impulso agora e adoraria ver sua adoção mais ampla, também em conjunto com Fable e React / Feliz.

@theimowski Não utilizo o Fantomas há tempos.

Principalmente porque estava bagunçando meu código e tornando-o muito difícil de ler. Pela minha experiência até não estava jogando bem com Fable.React e Feliz.

Também não gosto do padrão do Fantomas, preciso verificá-lo novamente no futuro, quando souber como desejo que meu código seja indentado. Mas, pelo que eu olhei para o repo, não acho que ele vai atender às minhas necessidades principalmente por causa do Fantomas formatar o código dependendo do tamanho do contexto.

Por exemplo, se eu escrever algo assim:

type Test =
    {
        Prop1 : string
    }

Eu quero que continue assim e não se torne type Test = { Prop1 : string } só porque pode caber em uma única linha.

Talvez um disco não seja um bom exemplo, mas espero que você entenda o que quero dizer :)

@theimowski que ótimo ouvir, eu uso fantomas em meus projetos não-Fable. Vou ter que tentar novamente e ver como funciona.

@MangelMaxime obrigado pelo seu trabalho. De alguma forma, seu projeto Fulma me atacou para o mundo das fábulas. E eu realmente gosto da viagem.

Eu também gosto da lista única, mas também gosto de DU do que substituições de membros de classes. Às vezes me pergunto se o DU suporta override, então seria ótimo.
Porque eu já uso muito o Fable.React, mas ainda quero uma lista única, então criei um operador> e posso usá-lo assim:

let myView =
    div </> [
        Classes [ ""; ""; "" ]
        Children [
              ...
        ]
    ]

O operador é bastante simples e também tira proveito do DU, você pode verificar minha implementação aproximada aqui: https://github.com/albertwoo/Fun.LightForm/blob/master/src/Fun.LightForm.Fable/Fable.React. Extra.fs

@ Zaid-Ajaj Obrigado também pelo seu excelente trabalho, também comecei a converter alguns dos meus projetos para Feliz e Feliz.Material.

Obrigado!

Olá @MangelMaxime ,
Em primeiro lugar, obrigado! Francamente, eu não esperava que alguém desse uma olhada tão profunda na biblioteca que criei principalmente para dogfooding em projetos próprios e publiquei como efeito colateral. Engraçado como o pensamento inicial de "ei, como @ Zaid-Ajaj projetou Feliz é ótimo - vamos fazer o mesmo com Bulma" inchou.

Já criei um novo problema no Feliz.Bulma repo e adoro discutir como tornar a v2 ainda melhor. Parece que concordamos na maioria das questões, então não deve ser problema iniciar version2 branch. Analisar também quais são os planos de Zaid para o próximo lançamento importante pode ser útil para manter a API entre as versões principais mais recentes 100% compatível.

De qualquer forma, obrigado novamente - ótima análise!

Correndo o risco está reabrindo velhas discussões (! Sorry) Eu só queria acrescentar meus dois centavos sobre o debate recuo - este é (mais ou menos) o estilo seguimos que não permitem bom, identation consistente e seguir para o Fable.React estilo.

O único lugar ao qual isso não se aplica, no entanto, é para estilos não triviais. No entanto, em nossa experiência, isso representa apenas uma minoria de elementos (talvez 10-20%).

div [ ] [
    Hero.hero [ Hero.IsFullHeight ] [
        Hero.body [ ] [
            Container.container [ ] [
                img [ Src "img/fable-ionide.png"
                      Style [ Display DisplayOptions.Block
                              Width "auto"
                              Margin "auto" ]
                    ]
                br [ ]
                Heading.h3 [ Heading.Modifiers [ Modifier.TextAlignment (Screen.All, TextAlignment.Centered) ] ] [
                    str "Fable REPL"
                ]
                Heading.p [ Heading.IsSubtitle
                            Heading.Is5
                            Heading.Modifiers [ Modifier.TextAlignment (Screen.All, TextAlignment.Centered) ] ] [
                    str "is only available on desktop"
                ]
            ]
        ]
    ]
]

Além disso, se você usar o VS Code e algo como os colchetes do arco-íris, pode realmente ajudar a evitar a sobrecarga dos colchetes.

Outra coisa a considerar (talvez eu não tenha percebido isso acima) é que o GiraffeViewEngine usa o estilo Fable.React, ou seja, o elemento attr-list child-list, então apenas algo para pensar em termos de pessoas em transição entre os dois.

@ Zaid-Ajaj faz uma boa observação sobre Femto; há alguma razão pela qual isso não poderia ser implementado por componentes do estilo Fable.React, ou há algo inerente ao estilo Feliz ao qual o Femto está acoplado?

há alguma razão pela qual isso não poderia ser implementado por componentes do estilo Fable.React, ou há algo inerente ao estilo Feliz ao qual o Femto está acoplado?

@isaacabraham

Bibliotecas derivadas de Fable.React podem (e devem) adicionar metadados Femto, mas o próprio Fable.React não pode, porque tem uma combinação incompatível de react , react-dom e react-native . @MangelMaxime iniciou algum trabalho na divisão de pacotes, mas foi muito mais trabalhoso do que o esperado e provavelmente apresentará alterações importantes.

O que isso significa para bibliotecas derivadas de Fable.React é que elas devem incluir não apenas metadados npm de suas dependências, mas também versões compatíveis de react e react-dom porque Fable.React não não faça isso para eles (ao contrário de Feliz, que começa com react e react-dom modo que as extensões de Feliz adicionam apenas o que precisam). Infelizmente, ainda não vi nenhuma dessas bibliotecas tomar a iniciativa de melhorar a situação, mesmo que a solução já exista há algum tempo.

Com relação à formatação: Fantomas ainda não é capaz de formatar DSLs de uma maneira sensata e requer algum trabalho para fazer a formatação da lista configurável ou tem algum tipo de sinalizador "Feliz-mode" para formatar o React DSL usando bons padrões (que temos que vir com)

@ Zaid-Ajaj O problema com as dependências npm do React também é mais complexo, pois deve ser possível usar o preact em vez do react, sem quebrar o Femto.

@ l3m Sim, mas neste caso poderíamos fazer um acréscimo ao Femto para apoiar este caso. Ele me lembra de algo, então talvez tenhamos um problema sobre isso no repositório Femto (desculpe, não tenho tempo para verificar agora ^^)

@ l3m Preact pode ser usado no lugar do React, independentemente dos metadados Femto npm, porque você só precisa configurar aliases de importação de módulo de acordo com as diretrizes em Alternando para Preact quando tiver um aplicativo React existente.

Construir uma associação Preact pura (código 100% não React) não é o objetivo de Feliz e provavelmente deve ser implementada como uma biblioteca autônoma (Feliz.Preact que não depende de Feliz) a partir da qual associações somente Preact serão estendidas e usado no caso de você querer ficar longe de preact/compat

@ Zaid-Ajaj Estou usando o preact agora no meu aplicativo em Elmish e ele funciona bem. Eu estava apenas desconfiado sobre o Femto modificar packages.json se ele ver um nuget com React deps e eu queria destacar isso. Se isso não for um problema, tanto melhor.

Além disso, preact/compat não é mais necessário com o pré-ato mais recente, agora está no núcleo.

Correndo o risco de reabrir discussões antigas (desculpe!). Eu só queria adicionar meus dois centavos ao debate sobre recuo - este é (mais ou menos) o estilo que seguimos, que _faz_ permite uma identificação consistente e agradável para o Fable.React estilo.

Estou recuando o código de uma maneira muito semelhante ao de Isaac, e é bastante legível.
Eu sei que é difícil comparar com uma única forma de lista de fazer as coisas, mas o recuo da postagem original pode refletir algo semelhante ao seu código para comparar melhor.

Suponho que com F # 5 e a capacidade de "abrir classes estáticas", Feliz será capaz de suportar as sintaxes Html.div e div dependendo se você abre ou não a classe Html.

Isso seria muito legal. Seria possível tornar a classe Html estática para que ela pudesse ser aberta?

@ fc1943s Html , prop e style são classes estáticas. No entanto, não recomendo abri-los quando o recurso se tornar disponível no F # 5, porque o objetivo principal é ter um agrupamento e um ponto de entrada de descoberta para as funções disponíveis dentro dessa classe estática.

Como abrir a classe estática atrapalharia a descoberta? Você ainda pode digitar Feliz.Html.

@MaxWilsonMS Isso exigiu etapas adicionais para

E você escreveria Feliz.Html.div e então apagaria Feliz.Html. parte que parece estranha.

O que é bom é que as pessoas podem optar por abrir a classe estática ou não dependendo de suas preferências. :)

Não tenho certeza de como isso será útil, já que estou repetindo o que foi dito acima, mas experimentei como um iniciante completo:

  • Eu prefiro a abordagem de Feliz para indentação. Tentei abordagens semelhantes às sugeridas por Isaac, mas como os recuos não são todos 2s / 4s, "às vezes fico pensando nisso". Com Feliz, não perco tempo pensando nisso.

  • Se eu estivesse fazendo um elemento personalizado, faria da maneira Fable.React por enquanto. Dei uma olhada em construir coisas "do jeito Feliz" e fugi na hora (vou revisitar em algum momento!).

  • Eu acharia muito útil se a documentação priorizasse mais regularmente o aspecto "é como fazer uma propriedade customizada". Acho que é justo aceitar que, com apenas alguns autores, essas bibliotecas muitas vezes terão dificuldade em oferecer uma superfície de API para tudo o que é possível em CSS (por exemplo), então, se um iniciante ficar preso ao não conseguir definir "fontSize" em "em", ou não consegue encontrar "linhas de modelo de grade", ou não tem ideia de que eles devem fornecer algum argumento "Screen.All" - seria bom se eles soubessem como contorná-lo.

  • Acho que algumas melhorias na "Qualidade de vida" poderiam ser feitas para começar a eliminar algumas preocupações em potencial ... por exemplo, quando vejo longas listas verticais de acessórios de estilo em Feliz, começo a me perguntar se poderia haver um style.paddingHorizontal e style.paddingVertical . Sei que poderia fazer meus próprios ajudantes, mas este não é o lugar para fazer solicitações de recursos e que pode haver um desejo de evitar a introdução de coisas que não são padrão :)

  • Gosto da ideia de abrir a classe Html static no novo F #. De maneira crítica - meus colegas seriam capazes de entender isso quando olhassem por cima do meu ombro - e acho que é importante envolver mais pessoas. Para bancar o advogado do diabo: div não leva muito tempo para digitar e se eu precisar descobri-lo, este é o menor dos desafios que enfrentarei ao usar uma configuração que faz mágica enquanto escondendo Javascript, React, CSS etc. etc. de mim. Geralmente, porém, a descoberta da API é ótima.

  • Particularmente para transições / animações em estilos, o jeito Feliz faz muito sentido - muito bom de trabalhar.

  • De forma mais geral e provavelmente tangencial: o desafio que eu sempre acerto é em torno de entender onde termina o mundo Elmish e começa o mundo React. Isso não é realmente uma preocupação de nenhuma das bibliotecas - mas como eu não tenho a menor idéia sobre nada do React, o fato de eu poder obter um componente de função de Fable.React ou Feliz me confunde. Claro, aprender isso é minha responsabilidade - mas achei que valeria a pena mencionar. No momento, estou combinando as abordagens Elmish e React e acredito que isso resulte na re-renderização de todos os componentes na atualização - mas não sei a maneira "certa" de fazer as coisas para evitar isso porque exemplos de aplicativos totalmente construídos como este são difícil de encontrar. Às vezes, tenho a impressão de que deveria primeiro aprender a fazer aplicativos React de nível de produção em Javascript, depois me graduar para Fable / Elmish - mas isso parece uma barreira bastante significativa para entrar. Aviso: Não li a documentação do Feliz extensivamente, e ela é muito boa - provavelmente é por minha conta!

Obrigado a todos por compartilhar essas bibliotecas!

Obrigado por sua contribuição @ drk-mtr!

Se eu estivesse fazendo um elemento personalizado, faria da maneira Fable.React por enquanto. Dei uma olhada em construir coisas "do jeito Feliz" e fugi na hora (vou revisitar em algum momento!).

Sim, pode parecer muito assustador no início, mas é muito simples quando você se acostuma. Dito isso, é um custo inicial razoável para o desenvolvedor ao escrever bibliotecas. Acho isso uma troca bastante aceitável, uma vez que a grande maioria dos usuários não escreverá bibliotecas. Felizmente, o trabalho em si é bastante simples, e alguns procedimentos de copiar e colar podem acelerar muito do trabalho (ou, como alguns de nós, escrever geradores para construir o código para nós).

Eu acharia muito útil se a documentação priorizasse mais regularmente o aspecto "é como fazer uma propriedade customizada". Acho que é justo aceitar que, com apenas alguns autores, essas bibliotecas muitas vezes terão dificuldade em oferecer uma superfície de API para tudo o que é possível em CSS (por exemplo), então, se um iniciante ficar preso ao não conseguir definir "fontSize" em "em", ou não consegue encontrar "linhas de modelo de grade", ou não tem ideia de que eles devem fornecer algum argumento "Screen.All" - seria bom se eles soubessem como contorná-lo.

Apenas no caso de você não estar ciente (eu não poderia dizer a partir deste contexto):

Existe um método custom nos tipos prop e style .

Tudo que aceita ICssUnit pode ser definido como: style.fontSize (length.em 1)

Atualizar os documentos deve ser fácil o suficiente.

Acho que algumas melhorias na "Qualidade de Vida" poderiam ser feitas para começar a eliminar algumas preocupações em potencial ... por exemplo, quando vejo longas listas verticais de acessórios de estilo em Feliz, começo a me perguntar se poderia haver um style.paddingHorizontal e style.paddingVertical. Sei que poderia fazer meus próprios ajudantes, mas este não é o lugar para fazer solicitações de recursos e que pode haver um desejo de evitar a introdução de coisas que não são padrão :)

Qualquer coisa que ajude a tornar a experiência do usuário mais fácil é definitivamente bem-vinda em meu livro. Eu adoraria ver um problema / pr para qualquer uma dessas coisas que você notou!

De forma mais geral e provavelmente tangencial: o desafio que eu sempre acerto é em torno de entender onde termina o mundo Elmish e começa o mundo React. Isso não é realmente uma preocupação de nenhuma das bibliotecas - mas como eu não tenho a menor idéia sobre nada do React, o fato de eu poder obter um componente de função de Fable.React ou Feliz me confunde. Claro, aprender isso é minha responsabilidade - mas achei que valeria a pena mencionar. No momento, estou combinando as abordagens Elmish e React e acredito que isso resulte na re-renderização de todos os componentes na atualização - mas não sei a maneira "certa" de fazer as coisas para evitar isso porque exemplos de aplicativos totalmente construídos como este são difícil de encontrar. Às vezes, tenho a impressão de que deveria primeiro aprender a fazer aplicativos React de nível de produção em Javascript, depois me graduar para Fable / Elmish - mas isso parece uma barreira bastante significativa para entrar. Aviso: Não li a documentação do Feliz extensivamente, e ela é muito boa - provavelmente é por minha conta!

Minha experiência no mundo Fable (pré-Feliz) foi que escrevi tudo apenas como funções normais na maior parte, (como um componente para cada .fs). O resultado foi que perdi muitos dos benefícios que o sistema React oferece. Desde que comecei com Feliz, tive que me aprofundar mais em como funciona o React (o que é bom). Desde que me acostumei, eu realmente nem dou mais muita atenção ao assunto. Cada função que escrevo que faz renderização é um componente de função.

Se você realmente quer aprender a escrever React idiomático, acho que é realmente tão simples quanto restringir-se a não usar o Elmish e apenas usar componentes / ganchos de função em seu aplicativo de demonstração. Você pode acompanhar facilmente qualquer coisa escrita em JS já que a API é quase exatamente a mesma.

Ter um aplicativo de código aberto de qualidade escrito em Feliz é definitivamente algo que seria benéfico, eu criei um problema para ele há algum tempo # 67. A coisa mais próxima agora é provavelmente o próprio aplicativo docs ou o Electron Demo do @cmeeren .

Esta página foi útil?
0 / 5 - 0 avaliações

Questões relacionadas

Dzoukr picture Dzoukr  ·  9Comentários

alfonsogarciacaro picture alfonsogarciacaro  ·  11Comentários

cmeeren picture cmeeren  ·  6Comentários

cmeeren picture cmeeren  ·  13Comentários

7sharp9 picture 7sharp9  ·  7Comentários