Feliz: Мое путешествие с Фелизом | Сравнение Fable.React и Feliz

Созданный на 7 апр. 2020  ·  23Комментарии  ·  Источник: Zaid-Ajaj/Feliz

Привет,

этот выпуск предназначен для того, чтобы поделиться своим опытом с Фелиз и Фелиз.Бульма.

Этот отзыв основан на преобразовании Fable Repl из Fable.React + Fulma в Feliz + Feliz.Bulma .

В первом разделе я составлю список всего, что мне понравилось или с чем были проблемы при использовании Feliz. Я сделаю нечто подобное для Fable.React и Fulma, потому что они не лишены хорошей и менее хорошей стороны.

Затем, во второй раз, я постараюсь объяснить, как прошел мой опыт с Фелиз и Фелиз.Бульма. Цель состоит в том, чтобы поделиться с вами, как моя точка зрения изменилась с течением времени и почему.

Важный

Я знаю, что предмет, который я анализирую, чувствителен и что-то меня пугает. Но, пожалуйста, не забудьте оставить комментарий в позитивном ключе.

Оглавление

Система категоризации

Я попытался найти способ систематизировать свои отзывы, классические «плюсы и минусы» казались мне слишком ограниченными и агрессивными.

Вместо этого я буду использовать символы:

  • ✔️ представляет то, что мне понравилось
  • ⚠️ может представлять несколько вещей:

    • что-то, что вызвало у меня проблемы, потому что это отличается от того, к чему я привык

    • что-то, что можно улучшить

    • области, где пользователю нужно быть осторожным

  • ℹ представляет собой то, что сначала не было очевидным для меня, но не имеет прямого отношения к проблеме из-за Фелиза

В одной записи может быть несколько символов :)

Фелиз


✔️ Feliz и Fable.React можно смешивать, потому что Feliz является слоем поверх Fable.React


✔️ Код удобнее делать отступы по сравнению с Fable.React, потому что у нас есть только один список. За таблицей легче следить.

Щелкните здесь, чтобы узнать подробности

** Fable.React + Фульма **

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" ] ] ] ] ]
** Фелиз + Фелиз.Бульма **
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"
                    ]
                ]
            ]
        ]
    ]
]


✔️ Благодаря предыдущему пункту также легко перемещать код

feliz_move_code_demo


✔️ строго типизированный API для свойств DOM


✔️ Строго типизированный API для блоков CSS и CSS через ICSSUnits

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

✔️ Не загрязняет контекст, большинство вещей ниже Html.* или Prop.*


✔️ ⚠️ Feliz предлагает возможность избежать шума в коде

Щелкните здесь, чтобы узнать подробности

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" ]
Поскольку существует несколько способов написать один и тот же код, мы не можем просто «прочитать» код. Иногда нам нужно сделать шаг назад, чтобы понять внешний контекст. Это также делает код менее «согласованным», потому что не все написано одинаково.
Html.tr [
    Html.th "Steps"
    Html.th [
        prop.className "has-text-right"
        prop.text "ms"
    ]
]


⚠️ Он (пока) не поддерживает SSR


⚠️ Обнаружить перегрузку методов / свойств непросто (возможно, это ограничение Ionide, которого я не знаю)

Мне пришлось:

  • поэкспериментируйте самостоятельно и проверьте ошибку компилятора. Например, проверка, поддерживается ли prop.text 2.0 когда у меня был float.
  • посмотрите прямо на исходный код Feliz

⚠️ Усложните использование обратного вызова с более чем одним параметром. В Fable.REPL мне пришлось заставить uncurry использовать System.Func<_,_,_> .

Щелкните здесь, чтобы узнать подробности

[<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
Что дает этот код на вызывающей стороне:
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 предоставляет перегрузки для облегчения жизни пользователей, но за это приходится платить.

Для prop.onChange , например, 6 перегрузок в зависимости от того, что вы хотите слушать:

  • 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)

Это хорошо, потому что это позволяет людям не писать забавный код, но вместо этого вам нужно явно указать, какой тип события вы хотите.

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

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

✔️ ⚠️ Экосистема Feliz в основном безопасна по типам, но не препятствует написанию неверного кода. При использовании Feliz и одного из его расширений, таких как Feliz.Bulma, вы можете легко смешивать свойства, но при этом нужно быть осторожным.

Щелкните здесь, чтобы узнать подробности

Этот код кажется нормальным с точки зрения Фелиза и компилятора F #, но он не даст ожидаемого результата.

Html.p [
    text.isUppercase
    text.isItalic
    color.hasTextSuccess
    prop.text "Hello Feliz"
]
`text.isUppsercase`,` text.isItalic` и `color.hasTextSuccess` все выводят что-то вроде` ClassName "my-css-class`. Но в React только последний будет иметь эффект, поэтому в нашем случае код будет генерировать:
<p class="has-text-success">Hello Feliz<\p>
вместо того
<p class="is-uppercase is-italic has-text-success>Hello Feliz</p>
Решение - использовать `++`, предложенный Feliz.Bulma:
open Feliz.Bulma.Operators

Html.p [
    text.isUppercase
    ++ text.isItalic
    ++ color.hasTextSuccess
    prop.text "Hello Feliz"
]
Это все еще хорошая возможность, потому что это означает, что вы можете добавить поведение из другой библиотеки Feliz к «элементу Feliz», если они совместимы. Людям просто нужно быть осторожными с этим.


ℹ Сначала я подумал, что Фелиз не предлагал синтаксический сахар ev.Value который мы получаем при использовании Fable.React, но это не так.

Fable.React.Extension содержит синтаксический сахар, поэтому мы можем просто открыть его, чтобы не загрязнять контекст всеми функциями Fable.React.

open Feliz
open Fable.React.Extensions

ℹ Синтаксис не воспроизводит мышление HTML. Feliz - это скорее синтаксический сахар поверх React API, чем HTML. Фелиз мыслит категориями свойств, потому что даже children - это собственность.


Фелиз.Бульма

✔️ Хорошо интегрируется с Feliz


✔️ Легче делать отступ по сравнению с Fulma

Щелкните здесь, чтобы узнать подробности

// 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
]


✔️ Компоненты Bulma легко идентифицировать благодаря префиксу Bulma


✔️ ⚠️ Свойства доступны, но загрязняют контекст button.* , help.* , columns.* .

Теоретически люди должны использовать только один фреймворк CSS, поэтому я не думаю, что у них возникнут конфликты из-за общих свойств, таких как button.* , columns.* и т. Д.


✔️ Иерархия компонентов кажется достаточно простой для понимания

Bulma.card > Bulma.cardHeader > Bulma.cardHeaderTitle

Нажмите здесь, например

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

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


⚠️ Но некоторые компоненты не соответствуют одному и тому же соглашению

Bulma.passwordInput вместо Bulma.inputPassword

В этом случае вы не сможете легко изучить различные типы входных данных, потому что они не начинаются с одного и того же «префикса».


✔️ ⚠️ Feliz.Bulma позволяет легко смешивать поведения компонентов.

Щелкните здесь, чтобы узнать подробности

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
        ]
    ]
]
Это приятно, потому что, как видите, легко добавить новое поведение к существующему компоненту. Здесь мы добавили всплывающую подсказку к кнопке. Но это также означает, что вы можете написать недопустимый код, например:
Html.select [
    select.isFullwidth
]
вместо того
Bulma.select [
    select.isFullwidth
]
Fulma более строго относится к разделению компонентов и не позволяет вам смешивать поведение, если вы не передаете классы CSS через свойства CustomClass.


✔️ ⚠️ Feliz.Bulma не позволяет контролировать, какой HTML-элемент мы хотим выводить.

Например, у нас есть только Bulma.field который генерирует div . Однако иногда в качестве вывода требуется элемент p .


✔️ ⚠️ Фелиз.Бульма - точное отображение над Булмой

Это приятно, потому что он тонкий.

Но это также означает:

  • Вы должны достаточно хорошо знать Bulma или открыть документацию Bulma.
  • Это не избавляет от шумного кода

Например, при написании компонентов Bulma.tabs вы не следуете инструкциям и должны знать, что tabs нужно ul за которым следует li с a в нем.

Щелкните здесь, чтобы увидеть код

// 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 позволяет пользователю больше думать о «компонентах 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" ] ] ]

Примечание:

Fulma рассматривает tab как компонент и предлагает специальную оболочку для Tabs.Tab.* .

Фульме все еще нужно, чтобы вы знали, что необходим элемент a , но мы могли бы добавить его по умолчанию (это относится к некоторым компонентам).


✔️ ⚠️ Feliz.Bulma оказывает меньшее влияние на размер пакета в небольшом проекте, но будет иметь более значительное влияние при увеличении размера проекта.

Щелкните здесь, чтобы узнать подробности

Fulma использует множество DU для моделирования классов Bulma. Вы можете взглянуть на [Common.fs] (https://github.com/Fulma/Fulma/blob/2f99474cd6c793776001d07da009f7211be2f30c/src/Fulma/Common.fs); также каждый компонент имеет свои собственные DU. Для этого требуется, чтобы Fulma реализовала вызов функции parseOptions, которая, говоря простым языком, преобразует DU в классы. Feliz.Bulma использует более прямой подход, не создавая DSL поверх классов Bulma, а вместо этого напрямую выводя классы.

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

// Feliz
Bulma.column [
    column.isHalfDesktop
    column.isFullMobile
    prop.children [
        // ...
    ]
]
Благодаря прямому использованию классов и манипулированию списком свойств Feliz.Bulma не требует больших затрат на все DU и код, добавленные в Fulma. Однако для «присоединения» ко всем классам по-прежнему требуется дополнительный проход. Эта часть выполняется с помощью [модуль Feliz.Bulma.ElementBuilders.Helpers] (https://github.com/Dzoukr/Feliz.Bulma/blob/3ecbba1579d2a26281f24e6a6664b5d9c5222603/src/Feliz.BulmaLders.Element- эти функции встроены. Вот почему чем больше ваш проект, тем больше влияние на ваш код.


⚠️ Добавить новую цветовую поддержку непросто.

В Feliz.Bulma каждый компонент имеет собственную реализацию цвета, например button.isWarning , help.isWarning и т. Д.

Итак, если вы хотите добавить свой цвет, вам нужно реализовать все button.isMyNewColor , help.isMyNewColor

В Fulma, все они разделяют один и тот же тип цвета . См. Документацию


Басня.

✔️ Следует структуре HTML, если вы думаете о ней как о списке свойств и списке дочерних элементов.


⚠️ Трудно установить правила отступов, потому что нам нужно организовать двойной список, и, как правило, мы делаем много исключительных случаев.

Нажмите здесь, чтобы получить подробное объяснение

Пример:

// 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
Как вы можете видеть, имея дело с несколькими случаями в списке свойств, у нас есть несколько возможных синтаксисов. Если бы мне пришлось установить согласованный синтаксис в моем проекте, это было бы примерно так:
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... 🙄
Для сравнения в Фелизе я делаю:
// 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
            )
        ]
    ]
]
Итак, у меня сейчас только 2 случая, и я мог бы даже написать пустую версию на нескольких строках без лишнего шума, если бы мне действительно нужен был единственный способ структурирования кода Feliz.


⚠️ Большая часть API не типизирована

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

✔️ ⚠️ Некоторые части API являются типизированными, но не все, что означает, что код не согласован.


✔️ Поддерживает SSR


Фульма

✔️ Типобезопасный API

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

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

✔️ API Fulma легко изучить с помощью intellisense, если вы понимаете, как он структурирован


✔️ Фульма заставляет задуматься о компонентах


✔️ Упростите расширение поддерживаемого цвета благодаря IsCustomColor

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

✔️ ⚠️ Я думаю, что у него хорошая документация с примерами для всех компонентов, но не каждый компонент содержит одинаковый уровень информации


✔️ ⚠️ Влияние Фульмы на размер вашего бандла «стабильное». Этот момент был описан в разделе Фелиз.Бульма.


⚠️ Людям нелегко найти «Особых помощников».

  • CustomClass
  • Реквизит
  • Модификаторы

⚠️ Код Fulma добавляет шума в ваш код из-за строго типизированного DSL

Щелкните здесь, чтобы увидеть пример

// 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"
]


⚠️ Код Fulma не так просто правильно отформатировать, потому что он имеет много вложенных списков при объединении Props , Modifiers


Я работал над этим анализом в течение 2-3 недель и все еще могу продолжить. Но я должен положить этому конец, потому что на это уходит много времени, и я думаю, что теперь я лучше понимаю ситуацию.

Все это говорит о том, что можно было бы перечислить больше хороших моментов или проблем, особенно в отношении Fable.React и Fulma, но это также самая сложная часть для меня. Я использую их и проектирую более 3 лет, поэтому трудно иметь на них объективное представление.

Было ли преобразование трудным?

Конвертировать из Fable.React в Feliz было достаточно просто. Feliz совместим с Fable. React вы можете выполнять преобразование в прогрессивном режиме: файл на файл, компонент на компонент и т. Д.

Чтобы быть уверенным, что я преобразовал все, я удалил все инструкции open Fable.React и open Fable.React.Props . Я также удалил Fulma как зависимость.

Конечно, я забыл некоторые файлы, поэтому исправил ошибки компилятора, и все заработало.

Вы можете увидеть запрос слияния хостинг преобразования здесь .


Мое путешествие с Фелиз

Поначалу открывая для себя Feliz, я защищался, потому что он не следовал «пути HTML / JSX», и это альтернативный проект для вещей, в которые я вложил много усилий.

Затем я вспомнил, как работают инновации и что позволяет нам расти. Например, благодаря тому, что сегодня у нас есть Эльмиш.

Еще я вспомнил, что JSX - это не «настоящий React API», действительно, React API - это React.createElement . JSX - это синтаксический сахар поверх него, то же самое касается Fable.React и Feliz. Я настаиваю на этом, потому что для меня это помогло мне двигаться вперед и интересоваться этим.

Чтобы протестировать Фелиза, я решил использовать проект среднего размера - Fable REPL. Fable REPL - не сложное приложение, но оно использует множество функций Fable и React.

Неполный список вещей, которые я протестировал благодаря этому решению:

  • Фелиз
  • Фелиз.Бульма
  • Насколько хорошо совместимы Feliz и Fable.React
  • Насколько легко перейти с Fable.React на Feliz
  • Использование функционального компонента с нативным вызовом DOM от Feliz
  • Как написать привязку и взаимодействие с нетривиальным пользовательским компонентом, написанным на JavaScript, способом Feliz
  • Переход от Фульмы к Фелиз.Бульма, чтобы я мог увидеть, какой эквивалент Фульмы может быть в экосистеме Фелиза

Когда я начал конвертацию, я был очень доволен тем, насколько легко было отформатировать код. Я думаю, что условию написания представления, основанного на Фелизе, было бы очень легко следовать. Чем больше растут мои приложения, тем больше я считаю важным форматирование кода.

Затем в середине я начал что-то раздражать, но не мог найти что.

Я закончил конвертировать Fable REPL в Feliz и не был уверен, нравится ли мне мой опыт работы с ним.

Только сейчас я пытаюсь завершить свой анализ и понимаю, что меня сдерживает. Это не столько связано с Фелиз, сколько с Фелиз.Бульма.

Важно помнить, что более 2,5 лет я работаю над Fulma и улучшаю ее. Fulma - один из моих самых больших проектов. Писать код с его помощью - это здорово, особенно потому, что все типизировано и заставляет думать в терминах компонентов.

Фелиз.Бульма придерживается другого подхода и моложе Фульмы; ему всего 5 месяцев.

С учетом всего сказанного, я думаю , что настало время перейти к моему выводу :)

Заключение

Я считаю, что самым большим преимуществом Фелиза является стиль единого списка. Об этом я много говорил, но для меня возможность легко форматировать свой код и не думать, с чего начать следующую строку, является большим плюсом.

Во-вторых, необходимо иметь типобезопасный API. Он более подробный, чем эквивалент CSS, но я думаю, что это хорошая функция. Я не делал с ним ничего особенного, поэтому не могу сказать, все ли поддерживается, но если это не так, мы всегда можем сделать пиар, чтобы это исправить :).

Feliz.Bulma показывает многообещающий способ расширения Feliz, но еще не достаточно зрел, чтобы быть прямым эквивалентом Fulma. Это то, что я хочу изучить с помощью @Dzoukr, и я надеюсь, что мы сможем найти золотую середину между простотой Feliz.Bulma и полнотой, но многословием Fulma.

3 года назад, 8 марта 2017 года , Томас, Евгений и я решили объединить Fable.Arch с Эльмишем. Это позволило нам как сообществу построить потрясающую экосистему, которая есть у нас сегодня.

Я думаю, что сейчас мы живем в похожий период с Feliz и Fable.React. Я думаю, что Фелиз получит больше поддержки, чем Fable.
но оба будут сосуществовать вместе. В конце концов, они оба хорошо работают вместе, это означает, что люди могут выбрать тот, который им больше нравится.

Спасибо, что прочитали мой анализ, и я буду рад обсудить его с вами в разделе комментариев :)

awesome blog

Самый полезный комментарий

Привет @MangelMaxime!

Прежде всего, я хочу поблагодарить вас за то, что вы потратили время и силы не только на то, чтобы опробовать Feliz на проекте, но и на написание такого обширного анализа. Я уверен, что многие пользователи Fable найдут его очень полезным и информативным.

Я очень ценю такую ​​обратную связь и открытость и считаю, что это крайне важно для того, чтобы сделать эту библиотеку наилучшей, какой она может быть вместе с ее экосистемой: молитесь:

Я расскажу о Feliz.Bulma чуть позже, потому что я считаю, что это особенное место в экосистеме. Сначала я отвечу на некоторые проблемы, которые у вас есть, которые уже можно исправить без изменений в библиотеке.

Перегрузки onChange

вам нужно явно указать, какой тип события вы хотите.

На самом деле нет. Тип должен быть выведен из вашего типа сообщения следующим образом:

prop.onChange (ChangeGistToken >> dispatch)

В зависимости от типа ChangeGistToken будет выведена правильная перегрузка. Когда случай DU равен ChangeGistToken of string тогда будет выбрана перегрузка string -> unit . Но это также может быть FileSelected of File когда тип ввода file в этом случае будет выведена перегрузка File -> unit и так далее и тому подобное. Вам не нужен синтаксический сахар ev.Value от Fable.React

prop.onCheckedChange будет удален, поскольку onChange обрабатывает логический регистр

Обратные вызовы с несколькими аргументами

@Shmew уже затронул эту проблему, и это действительно способ решить эти типы свойств. Важно понимать, что при реализации расширений Feliz вы можете выполнять преобразования перед установкой свойства:

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

Следуя этой логике, вы можете внутренне преобразовать 'A -> 'B -> unit в Func<'A, 'B, unit> :

[<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

Это более явная версия, чем та, которая предоставляется @Shmew (как вы знаете, с привязками, вам всегда нужно немного поиграть с ними, чтобы получить их правильно). Рабочий пример этого здесь

Безопасность типов в экосистеме Feliz

Как упоминалось выше @Shmew : правильное расширение Feliz не позволяет вам писать код, который ведет себя неправильно, а ограничение типов свойств позволяет вам реализовать это. Вы можете либо создать свойства, которые возвращают IReactProperty например Feliz.Recharts, что обеспечивает обратную совместимость с существующими свойствами в prop либо вы можете реализовать более специализированный тип ISpecialiedProperty для поддержки только подмножество свойств при дублировании имен некоторых свойств из IReactProperty для обеспечения обратной совместимости:

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

Это то, что я делал с Feliz.AntDesign, например здесь, где я дублирую некоторые свойства, чтобы сделать их доступными из точки входа button (позже я мог бы сделать свойства более специализированными с помощью строгих типов, но сначала , обратная совместимость - это то, с чего вы впервые начинаете). Я считаю, что Feliz.MaterialUI делает аналогичные вещи для расширения IReactProperty а не для специализации свойств. Это подводит нас к делу Фелиз.Бульма.

Дело Фелиз Бульма

До того, как был реализован Feliz.Bulma, я думал, что нет необходимости создавать расширения Feliz для фреймворков, содержащих только CSS. Feliz позволяет очень легко составлять несколько имен классов и работать с ними по условию. Теперь с неявным yield в F # стало еще проще. Объедините это с TypedCssClasses, и вы

Однако после того, как я увидел реализацию Feliz.Bulma от @Dzoukr, это Bulma.button , Bulma.card и т. Д. Их действительно приятно использовать. Что касается модификаторов, я думаю, что подход Романа легко использовать, но, как вы сказали, иногда он может давать интересные результаты. Конечно, в этом отделе есть еще что улучшить, и в любом случае я думаю, что Роман проделал огромную работу, следуя видению Фелиза, создав что-то легкое для изучения и использования.

Пакетное управление Feliz

Я просто хочу указать на огромную разницу в текущих Fable.React и Feliz : в управлении версиями пакетов. Версии Feliz все ее экосистемы библиотек Фемто -Совместимы , которые снимают нагрузку от пользователей , чтобы вручную установить необходимые пакеты НПХ или знать , какую версию они совместимы с. Это не похоже на многие, если не на все производные библиотеки Fable.React и даже на сам Fable.React , где это не так.

Планы на будущее

  • Написание (и даже более) документации о том, как создавать расширения в стиле Feliz и чего следует придерживаться.
  • Создание анализатора Feliz для самого Feliz, но, возможно, для его экосистемы

Все 23 Комментарий

Отличный анализ.

Лично для меня, даже несмотря на то, что форматирование двойного списка иногда является проблемой, я чувствую, что глядя на код Fable.React, я лучше вижу структуру нижележащего html. Это div [.., а не Html.div [.... (и т. Д.). Это не так уж важно, но моему мозгу, кажется, легче разобрать Fable.React.

Отличное написание @MangelMaxime , вы сделали много хороших

Из того, что я собрал, главный вывод - это в основном проблемы с тем, как расширяется библиотека, и проблемы, которые возникают, когда они не выполняются оптимально.

Я могу определенно согласиться с тем, что написание библиотек для Feliz возлагает гораздо больше усилий на разработчика, но, на мой взгляд, это стоит усилий, когда полученный api так приятно использовать.

Я написал изрядное количество расширений для Feliz поэтому я думаю, что могу дать некоторое представление о том, как смягчить многие из этих проблем:

Перегрузки

Я использую Visual Studio, и у меня нет проблем с поиском доступных мне перегрузок, может быть, это что-то, что можно улучшить с помощью ионидов?

Мы могли бы просто сделать prop.text единственной встроенной функцией, которая требует члена toString и принимает любой тип?

Обратные вызовы:

Вместо того:

[<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

Напишите:

[<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)

Затем вы можете вызвать свойство, как вы ожидаете, на стороне клиента.

Написание неверного кода

Я не видел, чтобы кто-то еще применял это в той степени, в какой я применяю в некоторых из моих библиотек, и может быть не совсем применим при расширении Html . Чтобы предотвратить это, вы добавите дополнительные типы интерфейса к определяемым вами свойствам, в моей самой большой Feliz library Feliz.Plotly Я настроил ее таким образом, что вы получите ошибку компилятора, если вы попробуйте использовать недействительное свойство. Единственный недостаток - это довольно большая работа для разработчика библиотеки.


Нажмите, чтобы развернуть

[<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

Точно так же вы можете реализовать в коде библиотеки что-то, чтобы «автоматически» комбинировать свойства, если хотите. Я не уверен, как это работает при использовании вещей за пределами вашей собственной библиотеки. Уловка заключается в том, чтобы сделать эти предметы другим типом, а не просто IReactProperty . Например, в Feliz.Plotly я делаю много вещей, где в зависимости от заданных конфигураций я изменяю / собираю / изменяю вводимые пользователем данные, чтобы должным образом преобразовать их в то, как должен выглядеть реальный код. В некоторых отношениях он действительно хорош для удаления вещей, которые всегда присутствуют.

Заключение

Я думаю, что большую часть боли, которую вы испытывали, можно было бы решить с помощью более качественной документации о том, как ориентироваться при написании ваших собственных библиотек способом "Feliz". Может быть, мы с @ Zaid-Ajaj поработаем над этим в будущем.

Спасибо @ l3m

Лично для меня, даже несмотря на то, что форматирование двойного списка иногда является проблемой, я чувствую, что глядя на код Fable.React, я лучше вижу структуру нижележащего html. Это div [.., а не Html.div [.... (и т. Д.). Это не так уж важно, но моему мозгу, кажется, легче разобрать Fable.React.

Действительно, переключить способ чтения кода непросто, и поэтому я упомянул, что мы не должны рассматривать его как HTML, в моем случае это помогло мне переключиться.

Я полагаю, что с F # 5 и возможностью «открывать статические классы» Фелиз сможет поддерживать синтаксис Html.div и div зависимости от того, открываете ли вы класс Html или нет.

Спасибо @Shmew за комментарий, да и вообще я забыл, что есть несколько типов «библиотек» для Feliz.

По поводу «Написание неверного кода» да, но в некоторых библиотеках мы действительно хотим «расширить» тип IReactProperty , чтобы избежать ситуации Fulma, когда вам нужно передать стандартные свойства через специальный помощник, например Props . Причина в том, что «компоненты» Bulma - это просто специализация над элементами DOM.

Это просто код, который по умолчанию внедряет класс в элемент HTML.

Bulma.button генерирует <div class="button">

В случае библиотеки, которая работает с «настоящими» компонентами (извините, не знаю, как назвать ее по-другому), ваше решение действительно правильное. Например, это подход, используемый Fable.ReactLeaflet, где я определяю конкретные свойства для каждого компонента. Таким образом, компоненты не обязательно должны поддерживать все стандартные свойства HTML.

По поводу вашего решения для "Обратных вызовов" не у меня не сработало. Но функция, переданная Fable, имела каррированные аргументы, и, если я не хотел писать что-то вроде myFunction(arg1)(arg2) мне приходилось форсировать необработанную версию через System.Func .

Если вы знаете, как заставить его работать, я буду рад увидеть PR, направленный на его исправление в https://github.com/fable-compiler/repl/pull/108/files.

Привет @MangelMaxime!

Прежде всего, я хочу поблагодарить вас за то, что вы потратили время и силы не только на то, чтобы опробовать Feliz на проекте, но и на написание такого обширного анализа. Я уверен, что многие пользователи Fable найдут его очень полезным и информативным.

Я очень ценю такую ​​обратную связь и открытость и считаю, что это крайне важно для того, чтобы сделать эту библиотеку наилучшей, какой она может быть вместе с ее экосистемой: молитесь:

Я расскажу о Feliz.Bulma чуть позже, потому что я считаю, что это особенное место в экосистеме. Сначала я отвечу на некоторые проблемы, которые у вас есть, которые уже можно исправить без изменений в библиотеке.

Перегрузки onChange

вам нужно явно указать, какой тип события вы хотите.

На самом деле нет. Тип должен быть выведен из вашего типа сообщения следующим образом:

prop.onChange (ChangeGistToken >> dispatch)

В зависимости от типа ChangeGistToken будет выведена правильная перегрузка. Когда случай DU равен ChangeGistToken of string тогда будет выбрана перегрузка string -> unit . Но это также может быть FileSelected of File когда тип ввода file в этом случае будет выведена перегрузка File -> unit и так далее и тому подобное. Вам не нужен синтаксический сахар ev.Value от Fable.React

prop.onCheckedChange будет удален, поскольку onChange обрабатывает логический регистр

Обратные вызовы с несколькими аргументами

@Shmew уже затронул эту проблему, и это действительно способ решить эти типы свойств. Важно понимать, что при реализации расширений Feliz вы можете выполнять преобразования перед установкой свойства:

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

Следуя этой логике, вы можете внутренне преобразовать 'A -> 'B -> unit в Func<'A, 'B, unit> :

[<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

Это более явная версия, чем та, которая предоставляется @Shmew (как вы знаете, с привязками, вам всегда нужно немного поиграть с ними, чтобы получить их правильно). Рабочий пример этого здесь

Безопасность типов в экосистеме Feliz

Как упоминалось выше @Shmew : правильное расширение Feliz не позволяет вам писать код, который ведет себя неправильно, а ограничение типов свойств позволяет вам реализовать это. Вы можете либо создать свойства, которые возвращают IReactProperty например Feliz.Recharts, что обеспечивает обратную совместимость с существующими свойствами в prop либо вы можете реализовать более специализированный тип ISpecialiedProperty для поддержки только подмножество свойств при дублировании имен некоторых свойств из IReactProperty для обеспечения обратной совместимости:

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

Это то, что я делал с Feliz.AntDesign, например здесь, где я дублирую некоторые свойства, чтобы сделать их доступными из точки входа button (позже я мог бы сделать свойства более специализированными с помощью строгих типов, но сначала , обратная совместимость - это то, с чего вы впервые начинаете). Я считаю, что Feliz.MaterialUI делает аналогичные вещи для расширения IReactProperty а не для специализации свойств. Это подводит нас к делу Фелиз.Бульма.

Дело Фелиз Бульма

До того, как был реализован Feliz.Bulma, я думал, что нет необходимости создавать расширения Feliz для фреймворков, содержащих только CSS. Feliz позволяет очень легко составлять несколько имен классов и работать с ними по условию. Теперь с неявным yield в F # стало еще проще. Объедините это с TypedCssClasses, и вы

Однако после того, как я увидел реализацию Feliz.Bulma от @Dzoukr, это Bulma.button , Bulma.card и т. Д. Их действительно приятно использовать. Что касается модификаторов, я думаю, что подход Романа легко использовать, но, как вы сказали, иногда он может давать интересные результаты. Конечно, в этом отделе есть еще что улучшить, и в любом случае я думаю, что Роман проделал огромную работу, следуя видению Фелиза, создав что-то легкое для изучения и использования.

Пакетное управление Feliz

Я просто хочу указать на огромную разницу в текущих Fable.React и Feliz : в управлении версиями пакетов. Версии Feliz все ее экосистемы библиотек Фемто -Совместимы , которые снимают нагрузку от пользователей , чтобы вручную установить необходимые пакеты НПХ или знать , какую версию они совместимы с. Это не похоже на многие, если не на все производные библиотеки Fable.React и даже на сам Fable.React , где это не так.

Планы на будущее

  • Написание (и даже более) документации о том, как создавать расширения в стиле Feliz и чего следует придерживаться.
  • Создание анализатора Feliz для самого Feliz, но, возможно, для его экосистемы

На самом деле нет. Тип должен быть выведен из вашего типа сообщения следующим образом:

prop.onChange (ChangeGistToken >> dispatch)

Действительно, я не думал об этом использовании.

Обратные вызовы с несколькими аргументами

@Shmew уже затронул эту проблему, и это действительно способ решить эти типы свойств.

Извините, я не видел конца фрагмента, предоставленного @Shmew. Я только что увидел подпись под декларацией и подумал, что нет, не работает.

Вы оба правы, мы можем выполнить преобразование перед передачей значения свойству.

Дело Фелиз Бульма

Да, TypedCssClasses в порядке, но по сравнению с Fulma или Feliz.Bulma он не может обеспечить такой же уровень функций. Потому что не все содержится в файле CSS.

Другое дело, что в режиме разработки у нас не всегда создается файл на диске, и я не уверен, поддерживает ли TypedCssClasses этот сценарий.

Я думаю, что Роман проделал огромную работу, следуя видению Фелиза, создав нечто легкое в освоении и использовании.

Я согласен, я просто думаю, что нам нужно добавить немного работы, проделанной в Fulma, чтобы сделать ее похожей. Как я уже сказал, нам нужно найти золотую середину между ними обоими.

Недостающие функции, которые я сейчас имею в виду, позволяют управлять элементом вывода и упрощают расширение набора поддерживаемых цветов. Я не думаю, что это добавит к этому много сложностей.

@ Zaid-Ajaj благодарим вас за отзывы и особенно за то, что указали мне правильное направление использования потенциала Фелиза.
Признаюсь, я действительно не читал документацию Feliz (это плохо, я знаю), так что, возможно, они задокументированы в ней. Если нет, думаю, мы знаем еще кое-что, что нужно добавить :)

Спасибо @MangelMaxime - отличное сравнение. Вы нигде не упомянули Fantomas по поводу автоматического форматирования кода - у вас была возможность поработать с Fantomas с Fable.React или Feliz?
В одном из моих проектов Fable, где я использую Fantomas, я включил автоматическое форматирование при сохранении файлов в Ionide и должен сказать, что это действительно повышение производительности - не нужно вообще беспокоиться о ручном форматировании!
Я действительно думаю, что сейчас Fantomas набирает обороты и хотел бы увидеть его более широкое распространение, в том числе вместе с Fable и React / Feliz.

@theimowski Я не использовал Фантомас несколько раз.

В основном потому, что это мешало моему коду и затрудняло его чтение. По моему опыту, не очень хорошо играл с Fable.React и Feliz.

Мне также не нравится значение Fantomas по умолчанию, мне нужно проверить его снова в будущем, когда я узнаю, как я хочу, чтобы мой код был отступом. Но, судя по тому, что я посмотрел на репо, я не думаю, что он будет соответствовать моим потребностям в основном из-за того, что Fantomas форматирует код в зависимости от размера контекста.

Например, если я напишу что-то подобное:

type Test =
    {
        Prop1 : string
    }

Я действительно хочу, чтобы он оставался таким, а не превращался в type Test = { Prop1 : string } только потому, что он может уместиться в одной строке.

Возможно, запись - не лучший пример, но я надеюсь, вы понимаете, о чем я :)

@theimowski , приятно слышать, я использую фантомы в своих проектах, не относящихся к Fable. Придется дать ему еще один шанс и посмотреть, как он с этим справится.

@MangelMaxime благодарит за вашу работу. Каким-то образом ваш проект Fulma напал на меня в сказочный мир. И мне очень нравится путешествие.

Мне также нравится единый список, но мне также нравится DU, чем переопределение членов классов. Иногда мне интересно, поддерживает ли DU переопределение, тогда было бы здорово.
Поскольку я уже много использую Fable.React, но все же хочу единый список, я создал оператор> и могу использовать его как:

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

Оператор довольно прост и также использует преимущества DU, вы можете проверить мою примерную реализацию здесь: https://github.com/albertwoo/Fun.LightForm/blob/master/src/Fun.LightForm.Fable/Fable.React. Extra.fs

@ Zaid-Ajaj Спасибо и за вашу отличную работу, я также начал преобразовывать некоторые из моих проектов в Feliz и Feliz.Material.

Спасибо!

Привет @MangelMaxime!
прежде всего спасибо! Честно говоря, я не ожидал, что кто-то так глубоко изучит библиотеку, которую я создал в основном для поддержки собственных проектов и опубликовал как побочный эффект. Забавно, как раздулась первоначальная мысль «эй, как @Zaid-Ajaj разработал Фелиза - давайте сделаем то же самое для Бульмы».

Я уже создал новую проблему в репозитории Feliz.Bulma и люблю обсуждать, как сделать версию 2 еще лучше. Похоже, у нас есть согласие по большинству вопросов, поэтому запустить ветку version2 не должно быть проблем. Также может быть полезно посмотреть, каковы планы Зейда на следующий крупный выпуск, чтобы API между последними основными версиями был на 100% совместимым.

В любом случае, еще раз спасибо - отличный анализ!

В риске возобновление старых дискуссий (! Извините) Я просто хотел бы добавить свои два цента на дискуссии отступа - это (более или менее) стилизации мы следуем , который действительно позволяют хорошему, в соответствии identation и следующая за Fable.React стиль.

Однако единственное место, к которому это не относится, - это нетривиальный стиль. Однако, по нашему опыту, это лишь небольшая часть элементов (возможно, 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"
                ]
            ]
        ]
    ]
]

Кроме того, если вы используете VS Code и что-то вроде Rainbow Brackets, это действительно может помочь избежать перегрузки скобок.

Еще кое-что, что следует учитывать (возможно, я пропустил это выше), это то, что GiraffeViewEngine использует стиль Fable.React, то есть элемент attr-list children-list, так что просто над чем подумать с точки зрения людей, переходящих между ними.

@ Zaid-Ajaj хорошо отзывается о Фемто; есть ли причина, по которой это не может быть реализовано компонентами стиля Fable.React, или есть что-то присущее стилю Feliz, с которым связан Femto?

есть ли причина, по которой это не может быть реализовано компонентами стиля Fable.React, или есть что-то присущее стилю Feliz, с которым связан Femto?

@isaacabraham

Библиотеки, производные от Fable.React, могут (и должны) добавлять метаданные Femto, но сам Fable.React не может, потому что он содержит несовместимое сочетание react , react-dom и react-native . @MangelMaxime начал некоторую работу по разделению пакетов, но это было намного больше, чем ожидалось, и, вероятно, внесет критические изменения.

Для библиотек, производных от Fable.React означает, что они должны включать не только метаданные npm своих зависимостей, но также включать совместимые версии react и react-dom потому что Fable.React этого не делает. не делайте этого для них (в отличие от Feliz, который начинается с react и react-dom так что расширения Feliz добавляют только то, что им нужно). К сожалению, я еще не видел, чтобы какая-либо из этих библиотек проявила инициативу по улучшению ситуации, даже если решение существует уже довольно давно.

Что касается форматирования: Fantomas еще не может разумно форматировать DSL и требует некоторой работы по настройке форматирования списка или имеет какой-то флаг «Feliz-mode» для форматирования React DSL с использованием хороших значений по умолчанию (которые мы должны придти вверх с)

@ Zaid-Ajaj Проблема с зависимостями React npm также более сложна, поскольку должна быть возможность использовать preact вместо реакции, не нарушая Femto.

@ l3m Действительно, но в этом случае мы могли бы сделать дополнение к Femto для поддержки этого случая. Он кое-что меня помнит, так что, возможно, у нас есть проблема с этим в репозитории Femto (извините, у меня нет времени проверить прямо сейчас ^^)

@ l3m Preact можно использовать вместо React независимо от метаданных Femto npm, потому что вам нужно только настроить псевдонимы импорта модулей в соответствии с рекомендациями в Переход на Preact, когда у вас есть существующее приложение React.

Создание чистой привязки Preact (100% не-React-код) не является целью Feliz и, вероятно, должно быть реализовано как отдельная библиотека (Feliz.Preact, которая не зависит от Feliz), из которой будут расширены привязки только для Preact и используется, если вы хотите держаться подальше от preact/compat

@ Zaid-Ajaj Я использую preact прямо сейчас в своем приложении Elmish, и он работает хорошо. Я просто опасался, что Фемто изменит packages.json, если увидит nuget с зависимостями React, и я хотел это выделить. Если это не проблема, тем лучше.

Кроме того, preact/compat больше не требуется с последней версией preact, теперь она находится в ядре.

Рискуя возобновить старые дискуссии (извините!) Я просто хотел добавить свои два цента к дебатам об отступах - это (более или менее) стиль, которому мы следуем, и который _ позволяет_ обеспечить красивую, последовательную идентификацию и отслеживание для Fable.React стиль.

Я делаю отступы в коде, очень похожем на код Исаака, и он довольно удобочитаем.
Я знаю, что его сложно сравнивать с одним списком, но отступ в исходном посте может отражать что-то похожее на его код, чтобы лучше сравнить.

Я полагаю, что с F # 5 и возможностью «открывать статические классы» Фелиз сможет поддерживать синтаксис Html.div и div зависимости от того, открываете ли вы класс Html или нет.

Это было бы супер круто. Можно ли сделать класс Html статическим, чтобы его можно было открывать?

@ fc1943s Html , prop и style - все статические классы. Однако я не рекомендую открывать их, когда функция становится доступной в F # 5, потому что все дело в группировке и точке входа для обнаружения доступных функций в этом статическом классе.

Как открытие статического класса может помешать обнаружению? Вы все равно можете набрать Feliz.Html.

@MaxWilsonMS Это потребовало дополнительных шагов для вывода «окончательного» кода.

И вы должны написать Feliz.Html.div а затем стереть часть Feliz.Html. которая кажется странной.

Что хорошо, так это то, что люди могут выбирать, открывать статический класс или нет, в зависимости от своих предпочтений. :)

Не уверен, насколько это будет полезно, поскольку я повторяю то, что было сказано выше, но ощущаю себя полным новичком:

  • Я предпочитаю подход Фелиза для вдавливания. Я пробовал подходы, аналогичные тому, что предлагал Исаак, но поскольку отступы - это не все 2/4, «я иногда трачу время на размышления об этом». С Фелиз я вообще не думаю об этом.

  • Если бы я делал нестандартный элемент, я бы сейчас делал это способом Fable.React. Я взглянул на постройку вещей «по-Фелизски» и тут же убежал (в какой-то момент я еще вернусь!).

  • Было бы очень полезно, если бы в документации более регулярно уделялось приоритетное внимание аспекту «вот как сделать настраиваемое свойство». Я думаю, будет справедливо согласиться с тем, что, имея всего несколько авторов, эти библиотеки часто будут изо всех сил пытаться предложить поверхность API для всего, что возможно в CSS (например), поэтому, если новичок застрянет, когда они не могут установить "fontSize" в «em», или не могут найти «grid-template-rows», или понятия не имеют, что они должны предоставить аргумент «Screen.All» - было бы хорошо, если бы они знали, как это обойти.

  • Я думаю, что можно было бы внести некоторые улучшения в "Качество жизни", которые позволили бы избавиться от некоторых потенциальных проблем ... например, когда я смотрю на длинные вертикальные списки стилей в Feliz, я начинаю задаваться вопросом, может ли быть style.paddingHorizontal и style.paddingVertical . Я понимаю, что могу создавать своих собственных помощников, но здесь не место для запросов функций, и что может быть желание избежать введения нестандартных вещей :)

  • Мне нравится идея открыть статический класс Html в новом F #. Важно то, что мои коллеги смогут понять это, когда посмотрят мне через плечо, и я думаю, что важно привлечь больше людей. Чтобы сыграть в адвоката дьявола: div не займет много времени, и если мне нужно это открыть, то это наименьшая из проблем, с которыми я столкнусь при использовании установки, которая творит магию, пока скрывая от меня Javascript, React, CSS и т. д. и т. д. Тем не менее, в целом API-интерфейс легко обнаруживается.

  • В частности, для переходов / анимации в стилях способ Feliz имеет большой смысл - с ним действительно приятно работать.

  • В более общем плане и, вероятно, косвенно: проблема, с которой я постоянно сталкиваюсь, заключается в том, чтобы понять, где заканчивается мир Elmish и начинается мир React. На самом деле это не проблема ни одной из библиотек, но поскольку я не имею ни малейшего представления о чем-либо React, меня смущает тот факт, что я могу получить функциональный компонент от Fable.React или Feliz. Конечно, я обязан научиться этому, но я подумал, что об этом стоит упомянуть. В настоящее время я смешиваю подходы Elmish и React, и я считаю, что это приводит к повторному рендерингу всех компонентов при обновлении, но я не знаю «правильного» способа сделать что-то, чтобы этого избежать, потому что примеры полностью построенных приложений, подобных этому: трудно найти. Иногда у меня складывается впечатление, что я должен сначала научиться создавать производственные приложения React на Javascript, а затем перейти на Fable / Elmish, но это кажется довольно серьезным барьером для входа. Отказ от ответственности: я не читал подробно документацию Feliz, и это действительно хорошо - так что это, вероятно, на мне!

Спасибо всем за то, что поделились этими библиотеками!

Спасибо за ваш вклад @ drk-mtr!

Если бы я делал нестандартный элемент, я бы сейчас делал это способом Fable.React. Я взглянул на постройку вещей «по-Фелизски» и тут же убежал (в какой-то момент я еще вернусь!).

Да, поначалу это может показаться довольно пугающим, но, когда вы привыкнете, это довольно просто. При этом для разработчика это небольшая начальная стоимость при написании библиотек. Я считаю это довольно приемлемым компромиссом, поскольку подавляющее большинство пользователей не собираются писать библиотеки. К счастью, сама работа довольно проста, и некоторое копирование + вставка может ускорить большую часть работы (или, как некоторые из нас, написание генераторов для создания кода для нас).

Было бы очень полезно, если бы в документации более регулярно уделялось приоритетное внимание аспекту «вот как сделать настраиваемое свойство». Я думаю, будет справедливо согласиться с тем, что, имея всего несколько авторов, эти библиотеки часто будут изо всех сил пытаться предложить поверхность API для всего, что возможно в CSS (например), поэтому, если новичок застрянет, когда они не могут установить "fontSize" в «em», или не могут найти «grid-template-rows», или понятия не имеют, что они должны предоставить аргумент «Screen.All» - было бы хорошо, если бы они знали, как это обойти.

На всякий случай, если вы не в курсе (я не мог точно сказать из этого контекста):

Существует метод custom style типов prop и style .

Все, что принимает ICssUnit может быть установлено как: style.fontSize (length.em 1)

Обновление документов должно быть достаточно простым.

Я думаю, что можно было бы внести некоторые улучшения в "Качество жизни", которые позволили бы избавиться от некоторых потенциальных проблем ... например, когда я смотрю на длинные вертикальные списки атрибутов стиля в Feliz, я начинаю задаваться вопросом, могут ли быть style.paddingHorizontal и style.paddingВертикальный. Я понимаю, что могу создавать своих собственных помощников, но здесь не место для запросов функций, и что может быть желание избежать введения нестандартных вещей :)

В моей книге определенно приветствуется все, что помогает облегчить взаимодействие с пользователем. Я бы хотел увидеть проблему / PR по любой из этих замеченных вами вещей!

В более общем плане и, вероятно, косвенно: проблема, с которой я постоянно сталкиваюсь, заключается в том, чтобы понять, где заканчивается мир Elmish и начинается мир React. На самом деле это не проблема ни одной из библиотек, но поскольку я не имею ни малейшего представления о чем-либо React, меня смущает тот факт, что я могу получить функциональный компонент от Fable.React или Feliz. Конечно, я обязан научиться этому, но я подумал, что об этом стоит упомянуть. В настоящее время я смешиваю подходы Elmish и React, и я считаю, что это приводит к повторному рендерингу всех компонентов при обновлении, но я не знаю «правильного» способа сделать что-то, чтобы этого избежать, потому что примеры полностью построенных приложений, подобных этому: трудно найти. Иногда у меня складывается впечатление, что я должен сначала научиться создавать производственные приложения React на Javascript, а затем перейти на Fable / Elmish, но это кажется довольно серьезным барьером для входа. Отказ от ответственности: я не читал подробно документацию Feliz, и это действительно хорошо - так что это, вероятно, на мне!

Мой опыт в мире Fable (до Feliz) заключался в том, что я писал все как обычные функции по большей части (например, по одному компоненту для каждого .fs). В результате я потерял множество преимуществ, которые предоставляет система React. Начиная с Feliz, мне пришлось по-настоящему погрузиться в то, как работает React (хорошая вещь, я думаю). С тех пор, как я к этому привык, я даже не задумываюсь об этом осознанно. Каждая функция, которую я пишу, которая выполняет рендеринг, является функциональным компонентом.

Если вы действительно хотите научиться идиоматически писать React, я думаю, что это действительно так же просто, как ограничить себя тем, чтобы не использовать Elmish, а использовать только функциональные компоненты / хуки в вашем демонстрационном приложении. Вы можете легко следить за всем, что написано на JS, поскольку API почти такой же.

Наличие качественного приложения с открытым исходным кодом, написанного на Feliz, определенно было бы полезно, я недавно создал для него проблему # 67. Сейчас ближе всего к нему, вероятно, само приложение docs или демо - Electron от @cmeeren .

Была ли эта страница полезной?
0 / 5 - 0 рейтинги