Bonjour,
ce numéro est pour partager mon expérience avec Feliz et Feliz.Bulma.
Ce retour est basé sur la conversion de Fable Repl de Fable.React + Fulma en Feliz + Feliz.Bulma .
Dans la première section, je vais faire une liste de toutes les choses que j'ai aimées ou avec lesquelles j'ai eu des problèmes lors de l'utilisation de Feliz. Je ferai quelque chose de similaire pour Fable.React et Fulma car ils ne sont pas exempts de bonne et de moins bonne partie.
Ensuite, dans un second temps, je vais essayer d'expliquer comment s'est déroulée mon expérience avec Feliz et Feliz.Bulma. Le but est de partager avec vous comment mon point de vue a évolué au fil du temps et pourquoi.
Important
Je sais que le sujet que j'analyse est sensible et que quelque chose me fait peur. Mais s'il vous plaît, n'oubliez pas de garder la section des commentaires positive.
J'ai essayé de trouver un moyen d'organiser mes retours, le classique "pour vs contre" me paraissait trop limité et agressif.
Au lieu de cela, j'utiliserai des symboles :
Une même entrée peut avoir plusieurs symboles :)
✔️ Feliz et Fable.React peuvent être mélangés car Feliz est une couche au-dessus de Fable.React
✔️ Le code est plus confortable à indenter par rapport à Fable.React car nous n'avons qu'une seule liste. Il est plus facile de suivre la tabulation.
Cliquez ici pour plus de détails
**Fable.Réagir + 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"
]
]
]
]
]
]
✔️ Grâce au point précédent, il est également facile de déplacer le code
✔️ API fortement typée pour les propriétés DOM
✔️ API fortement typée pour les unités CSS et CSS via ICSSUnits
style.marginLeft (length.em 0.5)
style.width (length.percent (model.PanelSplitRatio * 100.))
✔️ Ne pollue pas le contexte, la plupart des choses sont sous Html.*
ou Prop.*
✔️ ⚠️ Feliz offre la possibilité d'éviter le bruit dans le code
Cliquez ici pour plus de détails
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" ]
Comme il existe plusieurs façons d'écrire le même code, nous ne pouvons pas simplement « lire » le code. Nous avons parfois besoin de prendre du recul pour comprendre le contexte extérieur. Cela rend également le code moins "cohérent" car tout n'est pas écrit de la même manière.Html.tr [
Html.th "Steps"
Html.th [
prop.className "has-text-right"
prop.text "ms"
]
]
⚠️ Il ne supporte pas (encore) SSR
⚠️ Découvrir la surcharge méthodes/propriétés n'est pas facile (c'est peut-être une limitation d'Ionide que je ne connais pas)
J'ai dû:
prop.text 2.0
est pris en charge ou non lorsque j'avais un float.⚠️ Rendre plus difficile l'utilisation du rappel en prenant plus d'un paramètre. Dans Fable.REPL, j'ai dû forcer le uncurry en utilisant System.Func<_,_,_>
.
Cliquez ici pour plus de détails
[<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
Ce qui donne ce code côté appelant :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 propose des surcharges pour faciliter un peu la vie des utilisateurs, mais cela a un coût.
Pour prop.onChange
, comme 6 surcharges selon ce que vous voulez écouter :
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)
C'est bien parce que cela évite aux gens d'écrire "aucun" code amusant, mais la contrepartie est que vous devez dire quel type d'événement vous voulez explicitement.
// Feliz
prop.onChange (fun (e : Types.Event) -> e.Value |> ChangeGistToken |> dispatch)
// Fable.React
prop.onChange (fun e -> e.Value |> ChangeGistToken |> dispatch)
✔️ ⚠️ L'écosystème Feliz est principalement typé mais n'empêche pas d'écrire du code invalide. Lorsque vous utilisez Feliz et l'une de ses extensions comme Feliz.Bulma, vous pouvez facilement mélanger des propriétés, mais vous devez être prudent lorsque vous le faites.
Cliquez ici pour plus de détails
Ce code semble OK du point de vue de Feliz et du compilateur F# mais il ne vous donnera pas le résultat attendu.
Html.p [
text.isUppercase
text.isItalic
color.hasTextSuccess
prop.text "Hello Feliz"
]
`text.isUppsercase`, `text.isItalic` et `color.hasTextSuccess` produisent tous quelque chose comme `ClassName "my-css-class`. Mais dans React, seul le dernier aura un effet donc dans notre cas le code sera produire:<p class="has-text-success">Hello Feliz<\p>
à la place de<p class="is-uppercase is-italic has-text-success>Hello Feliz</p>
La solution à cela est d'utiliser le `++` proposé par Feliz.Bulma :open Feliz.Bulma.Operators
Html.p [
text.isUppercase
++ text.isItalic
++ color.hasTextSuccess
prop.text "Hello Feliz"
]
C'est toujours une bonne possibilité car cela signifie que vous pouvez ajouter un comportement provenant d'une autre bibliothèque Feliz à un "élément Feliz" s'ils sont compatibles. Les gens ont juste besoin d'y faire attention.ℹ Au début, je pensais que Feliz n'offrait pas le sucre de syntaxe ev.Value
que nous obtenons en utilisant Fable.React, mais ce n'est pas le cas.
Fable.React.Extension
héberge la syntaxe sugar, nous pouvons donc simplement l'ouvrir pour ne pas polluer le contexte avec toutes les fonctions Fable.React.
open Feliz
open Fable.React.Extensions
ℹ La syntaxe ne reproduit pas la façon de penser HTML. Feliz est plus un sucre de syntaxe au-dessus de l'API React que du HTML. Feliz pense en termes de propriétés car même children
est une propriété.
✔️ S'intègre bien avec Feliz
✔️ Plus facile à indenter par rapport à Fulma
Cliquez ici pour plus de détails
// 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
]
✔️ Il est facile d'identifier les composants Bulma
grâce au préfixe Bulma
✔️ ⚠️ Les propriétés sont accessibles mais polluent le contexte button.*
, help.*
, columns.*
.
En théorie, les gens ne devraient utiliser qu'un seul framework CSS, donc je ne pense pas qu'ils auront un conflit sur des propriétés génériques comme button.*
, columns.*
, etc.
✔️ La hiérarchie des composants semble assez facile à saisir
Bulma.card
> Bulma.cardHeader
> Bulma.cardHeaderTitle
Cliquez ici par exemple
Bulma.card [
Bulma.cardHeader [
prop.onClick (fun _ -> ToggleWidget title |> dispatch )
prop.children [
Bulma.cardHeaderTitle [
// ...
]
Bulma.cardHeaderIcon [
// ...
]
]
]
]
⚠️ Mais certains composants ne suivent pas la même convention
Bulma.passwordInput
au lieu de Bulma.inputPassword
Dans ce cas, vous ne pouvez pas facilement explorer les différents types d'entrées que vous avez car elles ne commencent pas par le même "préfixe".
✔️ ⚠️ Feliz.Bulma permet de mélanger facilement les comportements des composants.
Cliquez ici pour plus de détails
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
]
]
]
C'est bien car comme vous le voyez, il est facile d'ajouter un nouveau comportement à un composant existant. Ici, nous avons ajouté l'info-bulle de comportement à un bouton. Mais cela signifie également que vous pouvez écrire du code invalide comme :Html.select [
select.isFullwidth
]
à la place deBulma.select [
select.isFullwidth
]
Fulma est plus strict sur la séparation des composants et ne vous permet pas de mélanger le comportement à moins que vous ne passiez les classes CSS via les accessoires `CustomClass`.✔️ ⚠️ Feliz.Bulma ne permet pas de contrôler l'élément HTML que nous voulons sortir.
Par exemple, nous n'avons que Bulma.field
qui génère un div
. Cependant, vous voulez parfois un élément p
comme sortie.
✔️ ⚠️ Feliz.Bulma est une cartographie exacte sur Bulma
C'est bien parce que c'est mince.
Mais cela signifie aussi :
Par exemple, lorsque vous écrivez un composant Bulma.tabs
, vous n'êtes pas guidé et devez savoir que tabs
besoin de ul
suivi de li
avec un a
dedans.
Cliquez ici pour voir le code
// 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 permet à l'utilisateur de réfléchir davantage en termes de "composants 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" ] ] ]
Noter:
Fulma considère tab
comme un composant et propose un Tabs.Tab.*
wrapper spécifique.
Fulma a quand même besoin que vous sachiez qu'un élément a
est nécessaire, mais nous pourrions l'ajouter par défaut (c'est le cas pour certains composants).
✔️ ⚠️ Feliz.Bulma a un impact plus faible sur la taille du bundle sur un petit projet mais aura un impact plus important lorsque la taille du projet augmentera.
Cliquez ici pour plus de détails
Fulma utilise beaucoup de DU pour modéliser les classes Bulma. Vous pouvez consulter [Common.fs](https://github.com/Fulma/Fulma/blob/2f99474cd6c793776001d07da009f7211be2f30c/src/Fulma/Common.fs); aussi chaque composant a ses propres DU. Cela nécessite que Fulma implémente un appel de fonction `parseOptions` qui, en termes simples, convertit les DU en classes. Feliz.Bulma adopte une approche plus directe en ne créant pas de DSL au-dessus des classes Bulma mais en sortant directement les 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 [
// ...
]
]
Grâce à l'utilisation directe des classes et à la manipulation de la liste de propriétés, Feliz.Bulma n'a pas le coût élevé de tous les DU et du code ajoutés dans Fulma. Cependant, il a encore besoin d'un pass supplémentaire pour "rejoindre" toutes les classes. Cette partie se fait via [module Feliz.Bulma.ElementBuilders.Helpers](https://github.com/Dzoukr/Feliz.Bulma/blob/3ecbba1579d2a26281f24e6a6664b5d9c5222603/src/Feliz.Bulma/ElementBuilders.23) mais #L6- ces fonctions sont alignées. C'est pourquoi plus votre projet est grand, plus l'impact sur votre code est grand.⚠️ Il n'est pas facile d'ajouter un nouveau support de couleur.
Dans Feliz.Bulma, chaque composant possède sa propre implémentation de couleur comme button.isWarning
, help.isWarning
, etc.
Donc si vous voulez ajouter votre couleur, vous devez implémenter tous les button.isMyNewColor
, help.isMyNewColor
Dans Fulma, ils partagent tous le même type de couleur . Voir la documentation
✔️ Suit la structure HTML lorsque vous la considérez comme une liste de propriétés et une liste d'enfants
⚠️ Les règles d'indentation sont difficiles à établir car nous devons organiser une double liste et en général, nous faisons beaucoup de cas exceptionnels.
Cliquez ici pour une explication détaillée
Exemple:
// 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
Comme vous pouvez le voir juste en traitant quelques cas sur la liste des propriétés, nous avons plusieurs syntaxes possibles. Si je devais établir une syntaxe cohérente dans mon projet, ce serait quelque chose comme ça :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... 🙄
A titre de comparaison à Feliz, je fais :// 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
)
]
]
]
Je n'ai donc plus que 2 cas maintenant, et je pourrais même écrire la version vide sur plusieurs lignes sans trop de bruit si je veux vraiment une seule façon de structurer le code Feliz.⚠️ La plupart de l'API n'est pas typée
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
✔️ ⚠️ Une partie de l'API est typée mais pas toutes, ce qui signifie que le code n'est pas cohérent.
✔️ Il prend en charge la RSS
✔️ API de type sécurisé
Modifier.TextAlignment (Screen.All, TextAlignment.Centered)
Button.button [ Button.Color IsWhite ]
[ str "White" ]
✔️ L'API Fulma est facile à explorer via intellisense lorsque vous comprenez comment elle est structurée
✔️ Fulma vous oblige à penser en terme de composants
✔️ Facilitez l'extension de la couleur prise en charge grâce à IsCustomColor
// All you need to add `custom-purple` support to all your components is this line
let isCustomPurple = IsCustomColor "custom-purple"
✔️ ⚠️ Je pense qu'il a une bonne documentation avec des exemples pour tous les composants, mais tous les composants ne contiennent pas le même niveau d'information
✔️ ⚠️ L'impact de Fulma sur la taille de votre bundle est "stable". Ce point a été décrit dans la section Feliz.Bulma
⚠️ Les gens ne trouvent pas facilement les « aides spéciales ».
⚠️ Le code Fulma ajoute du bruit à votre code à cause du DSL fortement typé
Cliquez ici pour un exemple
// 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"
]
⚠️ Le code Fulma n'est pas si facile à formater correctement car il a beaucoup de listes imbriquées lorsque vous combinez Props
, Modifiers
J'ai travaillé sur cette analyse sur une période de 2-3 semaines et je pouvais encore continuer. Mais je dois y mettre fin car cela prend beaucoup de temps et je pense avoir maintenant une meilleure vision de la situation.
Tout ça pour dire qu'il pourrait y avoir d'autres bons points ou problèmes à lister notamment sur Fable.React et Fulma mais c'est aussi la partie la plus difficile pour moi. Je les utilise et les conçois depuis plus de 3 ans donc c'est difficile d'avoir un regard objectif sur eux.
La conversion de Fable.React à Feliz était assez facile à faire. Feliz étant compatible avec Fable.React vous pouvez faire la conversion de manière progressive fichier par fichier, composant par composant, etc.
Pour être sûr d'avoir tout converti, j'ai supprimé toutes les instructions open Fable.React
et open Fable.React.Props
. J'ai également supprimé Fulma en tant que dépendance.
Bien sûr, j'avais oublié certains fichiers, j'ai donc corrigé les erreurs du compilateur et cela fonctionnait.
Vous pouvez voir la demande de fusion hébergeant la conversion ici .
En découvrant Feliz au début, j'étais sur la défensive car il ne suivait pas la "voie HTML/JSX" et c'est un projet alternatif pour des choses dans lesquelles j'ai investi beaucoup d'efforts.
Puis je me suis souvenu que c'est ainsi que fonctionne l'innovation et que c'est ce qui nous permet de grandir. Par exemple, grâce à ce que nous avons aujourd'hui Elmish.
Une autre chose dont je me suis souvenu est que JSX n'est pas la "vraie API React", en effet l'API React est React.createElement
. JSX est un sucre de syntaxe en plus, il en va de même pour Fable.React et Feliz. J'insiste là-dessus car pour moi cela m'a permis d'avancer et d'être curieux à son sujet.
Pour tester Feliz, j'ai décidé d'utiliser un projet de taille moyenne qui est le Fable REPL. Fable REPL n'est pas une application complexe mais elle utilise beaucoup de fonctionnalités de Fable et React.
Une liste non exhaustive de choses que j'ai testé grâce à cette décision :
Lorsque j'ai commencé la conversion, j'ai été très satisfait de la facilité de formatage du code. Je pense que la convention pour écrire une vue basée sur Feliz pourrait être vraiment facile à suivre. Plus mes applications grandissent, plus je trouve le formatage du code important.
Puis au milieu, j'ai commencé à être ennuyé par quelque chose mais je ne pouvais pas trouver quoi.
J'ai fini de convertir Fable REPL en Feliz et je ne savais pas si j'aimais ou non mon expérience avec.
C'est seulement maintenant, que j'essaie de conclure mon analyse, que je comprends ce qui me retient. Ce n'est pas beaucoup lié à Feliz mais plus à Feliz.Bulma.
Il est important de se rappeler que depuis plus de 2,5 ans, je travaille sur Fulma et je l'améliore. Fulma est l'un de mes plus gros projets. Ecrire du code avec c'est super surtout parce que tout est typé et ça oblige à penser en terme de composants.
Feliz.Bulma adopte une approche différente et est plus jeune que Fulma ; il n'a que 5 mois.
Cela dit, je pense qu'il est maintenant temps de passer à ma conclusion :)
Je pense que le plus grand avantage de Feliz est le style de liste unique. C'est quelque chose que j'ai souvent mentionné, mais pour moi, pouvoir formater facilement mon code et ne pas avoir à penser par où commencer la ligne suivante est un gros plus.
Deuxièmement, l'API de type sécurisé est un incontournable. C'est plus détaillé que l'équivalent CSS mais je pense que c'est une bonne fonctionnalité. Je n'ai pas fait de trucs fantaisistes avec, donc je ne peux pas dire si tout est pris en charge, mais si ce n'est pas le cas, nous pouvons toujours faire un PR pour résoudre ce problème :).
Feliz.Bulma montre un moyen prometteur d'étendre Feliz mais n'est pas encore assez mature pour être un équivalent direct de Fulma. C'est quelque chose que je veux explorer davantage avec @Dzoukr et j'espère que nous pourrons trouver un juste milieu entre la simplicité de Feliz.Bulma et l'exhaustivité mais la verbosité de Fulma.
Il y a 3 ans, le 8 mars 2017 , Tomas, Eugene et moi avons décidé de fusionner Fable.Arch avec Elmish. Cela nous a permis en tant que communauté de construire l'incroyable écosystème que nous avons aujourd'hui.
Je pense que nous vivons actuellement une période similaire avec Feliz et Fable.React. Je pense que Feliz gagnera plus de terrain que Fable.React
mais les deux coexisteront ensemble. En fin de compte, les deux fonctionnent bien ensemble, ce qui signifie que les gens peuvent choisir celui qu'ils préfèrent utiliser.
Merci d'avoir lu mon analyse et je serai ravie d'en discuter avec vous dans la section commentaire :)
Excellente analyse.
Pour moi personnellement, même si le formatage de la double liste est parfois pénible, j'ai l'impression qu'en regardant le code Fable.React, je peux mieux voir la structure du code html sous-jacent. C'est div [.. , pas Html.div [.... (etc). Ce n'est pas grand chose, mais mon cerveau semble avoir une Fable.React plus facile à analyser.
Excellente écriture @MangelMaxime , vous avez fait beaucoup de bons points.
D'après ce que j'ai rassemblé, les principaux points à retenir sont principalement des problèmes liés à l'extension de la bibliothèque et aux problèmes qui surviennent lorsqu'ils ne sont pas exécutés de manière optimale.
Je suis tout à fait d'accord pour dire que l'écriture de bibliothèques pour Feliz
demande beaucoup plus de travail au développeur, mais à mon avis, cela en vaut la peine lorsque l'API résultante est si agréable à utiliser.
J'ai écrit un bon nombre d'extensions pour Feliz
, je pense donc pouvoir vous donner un aperçu de la façon d'atténuer bon nombre de ces problèmes :
J'utilise Visual Studio et n'ai aucun problème à trouver quelles surcharges sont disponibles pour moi, c'est peut-être quelque chose qui peut être amélioré du côté de l'ionide ?
Nous pourrions simplement faire en sorte que prop.text
soit une fonction en ligne unique qui nécessite le membre toString
et accepte n'importe quel type ?
À la place 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
Écrivez:
[<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)
Vous pouvez ensuite appeler la propriété comme vous vous en doutez du côté client.
Je n'ai vu personne d'autre appliquer cela dans la mesure où j'en ai dans certaines de mes bibliothèques, et peut ne pas être entièrement applicable lors de l'extension de Html
. La façon d'éviter cela est d'ajouter des types d'interface supplémentaires aux propriétés que vous définissez, dans ma plus grande bibliothèque Feliz.Plotly
Feliz
Feliz.Plotly
Je l'ai configuré de telle manière que vous obtiendrez une erreur de compilateur si vous essayez d'utiliser une propriété qui n'est pas valide. Le seul inconvénient est que c'est un peu de travail pour le développeur de la bibliothèque.
Cliquez pour agrandir
[<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
Dans la même veine, vous pouvez également implémenter des éléments dans le code de la bibliothèque pour combiner "automatiquement" les propriétés si vous le souhaitez. Je ne sais pas comment cela fonctionne lorsque vous utilisez des éléments en dehors de votre propre bibliothèque. L'astuce consiste à faire de ces éléments un type différent de celui de IReactProperty
. Par exemple, dans Feliz.Plotly
je fais beaucoup de choses où, en fonction des configurations données, je vais modifier/collecter/changer les entrées des utilisateurs pour les traduire correctement en ce à quoi le code réel doit ressembler. C'est vraiment sympa à certains égards pour enlever des choses qui sont toujours présentes.
Je pense qu'une grande partie de la douleur que vous avez eue pourrait être résolue avec une meilleure documentation sur la façon de naviguer dans l'écriture de vos propres bibliothèques à la manière "Feliz". Peut-être que @Zaid-Ajaj et moi pouvons travailler là-dessus à l'avenir.
Merci @l3m
Pour moi personnellement, même si le formatage de la double liste est parfois pénible, j'ai l'impression qu'en regardant le code Fable.React, je peux mieux voir la structure du code html sous-jacent. C'est div [.. , pas Html.div [.... (etc). Ce n'est pas grand chose, mais mon cerveau semble avoir une Fable.React plus facile à analyser.
En effet, changer la façon de lire le code n'est pas facile et c'est pourquoi j'ai mentionné que nous ne devrions pas le voir comme HTML dans mon cas, cela m'a aidé à faire le changement.
Je suppose qu'avec F# 5 et la possibilité d'"ouvrir des classes statiques", Feliz pourra prendre en charge à la fois la syntaxe Html.div
et div
selon que vous ouvrez ou non la classe Html.
Merci @Shmew pour le commentaire et en effet j'avais oublié qu'il existe plusieurs types de "bibliothèques" pour Feliz.
A propos du "Writing invalid code" oui mais dans certaines bibliothèques, nous voulons "étendre" le type IReactProperty
afin d'éviter la situation Fulma où vous devez passer des propriétés standard via un assistant spécial comme Props
. La raison étant que les "composants" Bulma ne sont qu'une spécialisation sur les éléments DOM.
Ce ne sont que du code qui injecte une classe par défaut dans l'élément HTML.
Bulma.button
génère <div class="button">
Dans le cas d'une bibliothèque qui fonctionne sur des composants "réels" (désolé je ne sais pas comment la nommer différemment) votre solution est en effet la bonne. Par exemple, c'est l'approche adoptée par Fable.ReactLeaflet où je
À propos de votre solution pour les "Rappels", cela n'a pas fonctionné pour moi. Mais la fonction passée par Fable avait des arguments curry et à moins que je ne veuille écrire quelque chose comme myFunction(arg1)(arg2)
alors je devais forcer la version non curry via System.Func
.
Si vous savez comment le faire fonctionner, je serai heureux de voir un PR envoyé pour le corriger dans https://github.com/fable-compiler/repl/pull/108/files
Salut @MangelMaxime ,
Tout d'abord, je tiens à vous remercier d'avoir pris le temps et les efforts non seulement d'essayer Feliz sur un projet, mais aussi d'écrire une analyse aussi approfondie. Je suis sûr que beaucoup d'utilisateurs de Fable le trouveront très utile et informatif.
J'apprécie vraiment ce genre de retour d'information et d'ouverture et je pense qu'il est crucial pour que cette bibliothèque soit la meilleure possible avec son écosystème :pray:
Je vais parler de Feliz.Bulma dans un instant parce que je pense que c'est un peu spécial dans l'écosystème, d'abord je vais répondre à certains problèmes que vous avez qui peuvent déjà être corrigés sans changements dans la bibliothèque.
onChange
vous devez dire quel type d'événement vous voulez explicitement.
En fait, vous ne le faites pas. Le type doit être déduit de votre type de message comme suit :
prop.onChange (ChangeGistToken >> dispatch)
Selon le type de ChangeGistToken
, la surcharge correcte sera déduite. Lorsque le cas DU est ChangeGistToken of string
alors la surcharge de string -> unit
sera choisie. Mais cela peut aussi être FileSelected of File
lorsque le type d'entrée est file
auquel cas la surcharge File -> unit
sera déduite et ainsi de suite. Vous n'avez pas besoin du sucre de syntaxe de ev.Value
de Fable.React
prop.onCheckedChange sera supprimé car
onChange
gère déjà le cas booléen
@Shmew a déjà abordé le problème et c'est en effet le moyen de résoudre ces types de propriétés. Il est crucial de comprendre que lors de l'implémentation des extensions Feliz, vous pouvez effectuer des transformations avant de définir la propriété :
type myExtension =
static member inline extenstionProperty value =
// transform value here
let transformedValue = doWeirdStuffWith value
Interop.mkAttr "extenstionProperty" transformedValue
En suivant cette logique, vous pouvez convertir 'A -> 'B -> unit
en Func<'A, 'B, unit>
interne :
[<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
Il s'agit d'une version plus explicite que celle fournie par @Shmew (Comme vous le savez avec les fixations, vous devez toujours jouer un peu avec elles pour les obtenir correctement). Un exemple fonctionnel de ceci est ici
Comme @Shmew mentionné ci-dessus : une extension Feliz appropriée ne vous permet pas d'écrire du code qui se comporte de manière incorrecte et la contrainte des types de propriétés vous permet de l'implémenter. Vous pouvez soit créer des propriétés qui renvoient IReactProperty
comme le fait Feliz.Recharts qui permet une compatibilité descendante avec les propriétés existantes dans prop
ou vous pouvez implémenter un type plus spécialisé ISpecialiedProperty
pour prendre en charge seulement un sous-ensemble des propriétés tout en dupliquant les noms de certaines propriétés à partir de IReactProperty
pour assurer la rétrocompatibilité :
type felizExtension =
static member inline specializedProp (value: string) : ISpecializedProperty = unbox (prop.text value)
C'est ce que j'ai fait avec Feliz.AntDesign, par exemple ici où je duplique certaines propriétés pour les rendre disponibles à partir du point d'entrée button
(plus tard, je pourrais rendre les propriétés plus spécialisées avec des types stricts mais au début , la compatibilité descendante est l'endroit où vous commencez pour la première fois). Je crois que Feliz.MaterialUI fait des choses similaires pour étendre IReactProperty
plutôt que de spécialiser les propriétés. Cela nous amène au cas de Feliz.Bulma.
Avant l'implémentation de Feliz.Bulma, j'avais en tête l'idée qu'il n'était pas nécessaire de créer des extensions Feliz pour les frameworks CSS uniquement. Feliz facilite déjà la composition de plusieurs noms de classes et leur utilisation conditionnelle. Maintenant, avec les yield
implicites en F#, c'est encore plus facile. Combinez cela avec TypedCssClasses et vous supprimez le besoin d'implémenter un framework CSS uniquement. Coûts de mise en œuvre et de maintenance nuls. Utilisez simplement la feuille de style dactylographiée et utilisez la très bonne documentation du projet lui-même au lieu d'avoir à créer vous-même un site Web de documentation.
Cependant, après avoir vu l'implémentation de Feliz.Bulma par @Dzoukr, cela avait du sens : vous pourriez simplifier beaucoup le code en introduisant des "points d'entrée" spécifiques aux composants CSS d'un module de Bulma afin que vous obteniez Bulma.button
, Bulma.card
etc. Ils sont vraiment agréables à utiliser. En ce qui concerne les modificateurs, je pense que l'approche de Roman est facile à suivre et à utiliser mais comme vous l'avez dit, cela peut parfois donner des résultats intéressants. Il y a sûrement plus à améliorer dans ce département et en tout cas, je pense que Roman a fait un excellent travail en suivant la vision de Feliz en créant quelque chose de facile à apprendre et à utiliser.
Je veux juste souligner une énorme différence entre les Fable.React
et les Feliz
actuels : le versioning et la gestion des packages. Les versions de Feliz sont toutes ses bibliothèques d'écosystèmes compatibles Femto, ce qui évite aux utilisateurs d'avoir à installer manuellement les packages npm requis ou de savoir avec quelle version ils sont compatibles. Ceci est différent de beaucoup sinon de toutes les bibliothèques dérivées Fable.React
et même Fable.React
lui-même où ce n'est pas le cas.
En fait, vous ne le faites pas. Le type doit être déduit de votre type de message comme suit :
prop.onChange (ChangeGistToken >> dispatch)
En effet, je n'avais pas pensé à cet usage.
Rappels multi-arguments
@Shmew a déjà abordé le problème et c'est en effet le moyen de résoudre ces types de propriétés.
Oh désolé, je n'ai pas vu la fin de l'extrait fourni par @Shmew. Je viens de voir la déclaration de signature et j'ai dit que non ne fonctionnait pas.
Vous avez tous les deux raison, nous pouvons faire la transformation avant de passer la valeur à la propriété.
Oui, TypedCssClasses est correct mais comparé à Fulma ou Feliz.Bulma, il ne peut pas fournir le même niveau de fonctionnalités. Car tout n'est pas contenu dans le fichier CSS.
Une autre chose est qu'en mode dev, nous n'avons pas toujours le fichier généré sur le disque et je ne sais pas si TypedCssClasses prend en charge ce scénario.
Je pense que Roman a fait un excellent travail en suivant la vision de Feliz en créant quelque chose de facile à apprendre et à utiliser.
Je suis d'accord, je pense juste que nous devons ajouter un peu du travail effectué dans Fulma pour le rendre similaire. Comme je l'ai dit, nous devons trouver le juste milieu entre les deux.
Les fonctionnalités manquantes que j'ai à l'esprit en ce moment permettent de contrôler l'élément de sortie et facilitent l'extension de l'ensemble des couleurs prises en charge. Je ne pense pas que cela ajoutera beaucoup de complexité.
@Zaid-Ajaj merci pour les commentaires et surtout pour m'avoir indiqué la bonne direction sur la façon d'utiliser le potentiel de Feliz.
J'admets que je n'ai pas vraiment lu la documentation de Feliz (c'est mauvais je sais) alors peut-être qu'ils y sont documentés. Sinon, je suppose que nous savons quelques autres choses à ajouter :)
Merci @MangelMaxime - c'est une excellente comparaison. Vous n'avez mentionné Fantomas nulle part pour le formatage automatique du code - avez-vous eu l'occasion de travailler avec Fantomas avec Fable.React ou Feliz ?
Dans l'un de mes projets Fable où j'utilise Fantomas, j'ai activé le formatage automatique lors de l'enregistrement de fichiers dans Ionide et je dois dire que c'est vraiment un booster de productivité - pas besoin de s'inquiéter du formatage manuel du tout !
Je pense vraiment que Fantomas prend son élan maintenant et aimerait voir son adoption plus large, également en conjonction avec Fable et React / Feliz.
@theimowski Je n'ai pas utilisé Fantomas depuis quelques temps.
Principalement parce que cela perturbait mon code et le rendait vraiment difficile à lire. D'après mon expérience, je ne jouais pas bien avec Fable.React et Feliz.
Je n'aime pas non plus la valeur par défaut de Fantomas. Je devrai la vérifier à nouveau à l'avenir lorsque je saurai comment je veux que mon code soit indenté. Mais, d'après ce que j'ai regardé dans le référentiel, je ne pense pas qu'il répondra à mes besoins, principalement à cause du formatage du code par Fantomas en fonction de la taille du contexte.
Par exemple, si j'écris quelque chose comme ça :
type Test =
{
Prop1 : string
}
Je veux qu'il reste comme ça et ne devienne pas type Test = { Prop1 : string }
juste parce qu'il peut tenir sur une seule ligne.
Peut-être qu'un disque n'est pas un bon exemple, mais j'espère que vous comprenez ce que je veux dire :)
@theimowski c'est super à entendre, j'utilise des fantomas sur mes projets non-Fable. Je vais devoir lui donner un autre coup et voir comment il le gère.
@MangelMaxime merci pour votre travail. D'une manière ou d'une autre, votre projet Fulma m'a attaqué dans le monde de la fable. Et j'apprécie vraiment le voyage.
J'aime aussi la liste unique mais j'aime aussi les DU que les remplacements des membres des classes. Parfois, je me demande si DU prend en charge la dérogation, alors ce serait super.
Parce que j'utilise déjà beaucoup Fable.React mais que je veux toujours une liste unique, j'ai donc créé un opérateur> et je peux l'utiliser comme:
let myView =
div </> [
Classes [ ""; ""; "" ]
Children [
...
]
]
L'opérateur est assez simple et tire également parti de DU, vous pouvez vérifier ma mise en œuvre approximative ici : https://github.com/albertwoo/Fun.LightForm/blob/master/src/Fun.LightForm.Fable/Fable.React. Extra.fs
@Zaid-Ajaj Merci aussi pour votre excellent travail, j'ai également commencé à convertir certains de mes projets en Feliz et Feliz.Material.
Merci!
Salut @MangelMaxime ,
tout d'abord merci ! Franchement, je ne m'attendais pas à ce que quelqu'un ait un regard aussi approfondi sur la bibliothèque que j'ai créée principalement pour mes propres projets de dogfood et publiée comme effet secondaire. C'est drôle comme la pensée initiale de "hé, comment @Zaid-Ajaj a conçu Feliz est génial - faisons la même chose pour Bulma" s'est enflée.
J'ai déjà créé un nouveau problème dans le dépôt Feliz.Bulma
et j'adore discuter de la façon d'améliorer encore la v2. Il semble que nous ayons un accord sur la plupart des problèmes, donc cela ne devrait pas poser de problème de démarrer la branche version2
. Examiner également le plan de Zaid pour la prochaine version majeure pourrait être utile pour que l'API entre les dernières versions majeures soit 100% compatible.
Quoi qu'il en soit, merci encore - excellente analyse!
Au risque est la réouverture de vieilles discussions (désolé!) Je voulais juste ajouter mes deux cents sur le débat d'indentation - c'est (plus ou moins) le style que nous suivons ce qui ne permet de bien, cohérente et suivant indentation pour la Fable.React style.
Le seul endroit auquel cela ne s'applique pas tout à fait, cependant, est pour le style non trivial. Cependant, dans notre expérience, cela ne représente qu'une minorité d'éléments (peut-être 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"
]
]
]
]
]
De plus, si vous utilisez VS Code et quelque chose comme Rainbow Brackets, cela peut vraiment aider à éviter la surcharge de brackets.
Une autre chose à considérer (peut-être que j'ai raté cela ci-dessus) est que GiraffeViewEngine utilise le style Fable.React, c'est-à-dire l'élément attr-list children-list, donc juste quelque chose à penser en termes de transition entre les deux.
@Zaid-Ajaj fait un bon point sur Femto; y a-t-il une raison pour laquelle cela ne pourrait pas être implémenté par les composants de style Fable.React, ou y a-t-il quelque chose d'inhérent au style Feliz auquel Femto est couplé?
y a-t-il une raison pour laquelle cela ne pourrait pas être implémenté par les composants de style Fable.React, ou y a-t-il quelque chose d'inhérent au style Feliz auquel Femto est couplé?
@isaacabraham
Les bibliothèques dérivées de Fable.React pourraient (et devraient) ajouter des métadonnées Femto, mais le Fable.React lui-même ne le pourrait pas car il contient un mélange incompatible de react
, react-dom
et react-native
. @MangelMaxime a commencé à travailler sur le fractionnement des packages, mais c'était beaucoup plus de travail que prévu et introduira probablement des changements de rupture.
Cela signifie pour les bibliothèques dérivées de Fable.React
qu'elles doivent inclure non seulement les métadonnées npm de leurs dépendances, mais également les versions compatibles de react
et react-dom
car Fable.React ne Ne faites pas ça pour eux (contrairement à Feliz qui commence par react
et react-dom
pour que les extensions Feliz n'ajoutent que ce dont elles ont besoin). Malheureusement, je n'ai encore vu aucune de ces bibliothèques prendre l'initiative d'améliorer la situation, même si la solution existe depuis un certain temps déjà.
Concernant le formatage : Fantomas n'est pas encore capable de formater les DSL de manière sensée et nécessite un certain travail pour rendre le formatage de liste configurable ou avoir une sorte de drapeau "Feliz-mode" pour formater le React DSL en utilisant de bons paramètres par défaut (que nous devons venir avec)
@Zaid-Ajaj Le problème avec les dépendances React npm est également plus complexe car il devrait être possible d'utiliser preact au lieu de réagir, sans casser Femto.
@l3m En effet, mais dans ce cas, nous pourrions faire un ajout à Femto pour prendre en charge ce cas. Cela me rappelle quelque chose alors peut-être que nous avons un problème à ce sujet dans le dépôt Femto (désolé, je n'ai pas le temps de vérifier tout de suite ^^)
@l3m Preact peut être utilisé à la place de React quelles que soient les métadonnées Femto npm, car vous n'avez besoin de configurer les alias d'importation de module conformément aux directives de Basculer vers Preact que lorsque vous avez une application React existante.
Construire une pure liaison Preact (code 100% non-React) n'est pas l'objectif de Feliz et devrait probablement être implémenté en tant que bibliothèque autonome (Feliz.Preact qui ne dépend pas de Feliz) à partir de laquelle les liaisons Preact uniquement seront étendues et utilisé au cas où vous voudriez rester à l'écart de preact/compat
@Zaid-Ajaj J'utilise preact en ce moment dans mon application Elmish et cela fonctionne bien. Je me méfiais simplement de la modification par Femto de packages.json s'il voyait un nuget avec les deps React et je voulais le souligner. Si ce n'est pas un problème, tant mieux.
De plus, preact/compat
n'est plus nécessaire avec le dernier preact, il est maintenant dans le noyau.
Au risque de rouvrir de vieilles discussions (désolé !) Je voulais juste ajouter mes deux cents sur le débat sur l'indentation - c'est (plus ou moins) le style que nous suivons qui _permet_ une identification et un suivi agréables et cohérents pour la Fable.React style.
J'indente le code d'une manière très similaire à celle d'Isaac, et c'est assez lisible.
Je sais qu'il est difficile de comparer avec une seule liste de façons de faire les choses, mais l'indentation du message d'origine pourrait refléter quelque chose de similaire à son code pour mieux comparer.
Je suppose qu'avec F# 5 et la possibilité d'"ouvrir des classes statiques", Feliz pourra prendre en charge à la fois la syntaxe
Html.div
etdiv
selon que vous ouvrez ou non la classe Html.
Ce serait super cool. Serait-il possible de rendre la classe HTML statique afin qu'elle puisse être ouverte ?
@fc1943s Html
, prop
et style
sont toutes des classes statiques. Cependant, je ne recommande pas de les ouvrir lorsque la fonctionnalité devient disponible dans F# 5, car l'essentiel est d'avoir un regroupement et un point d'entrée de découverte des fonctions disponibles au sein de cette classe statique.
Comment l'ouverture de la classe statique entraverait-elle la découverte ? Vous pouvez toujours taper Feliz.Html.
@MaxWilsonMS Cela nécessitait des étapes supplémentaires pour sortir le code "final".
Et vous écririez Feliz.Html.div
puis effaceriez la partie Feliz.Html.
qui semble étrange.
Ce qui est bien, c'est que les gens peuvent choisir d'ouvrir la classe statique ou non en fonction de leurs préférences. :)
Je ne sais pas à quel point cela sera utile car je me fais l'écho de ce qui a été dit ci-dessus, mais des expériences en tant que débutant complet :
Je préfère l'approche Feliz pour l'indentation. J'ai essayé des approches similaires à celles suggérées par Isaac, mais parce que les retraits ne sont pas tous des 2s/4, "je passe parfois du temps à y penser". Avec Feliz, je n'y pense pas du tout.
Si je créais un élément personnalisé, je le ferais à la manière Fable.React pour le moment. J'ai jeté un coup d'œil à la construction de choses "à la manière de Feliz" et je me suis enfui instantanément (je reviendrai à un moment donné!).
Je trouverais vraiment utile que la documentation priorise plus régulièrement l'aspect "voici comment créer une propriété personnalisée". Je pense qu'il est juste d'accepter qu'avec seulement quelques auteurs, ces bibliothèques vont souvent avoir du mal à offrir une surface d'API pour tout ce qui est possible en CSS (par exemple), donc si un débutant se bloque alors qu'il ne peut pas définir "fontSize" dans "em", ou ne peuvent pas trouver "grid-template-rows", ou n'ont aucune idée qu'ils sont censés fournir un argument "Screen.All" - ce serait bien s'ils savaient comment le contourner.
Je pense que certaines améliorations de la "qualité de vie" pourraient être apportées et commencer à réduire certains problèmes potentiels... par exemple, lorsque je regarde de longues listes verticales d'accessoires de style dans Feliz, je commence à me demander s'il pourrait y avoir un style.paddingHorizontal
et style.paddingVertical
. Je me rends compte que je pourrais créer mes propres aides, et ce n'est pas l'endroit pour faire des demandes de fonctionnalités cependant, et qu'il peut y avoir un désir d'éviter d'introduire des choses qui ne sont pas standard :)
J'aime l'idée d'ouvrir la classe statique Html
dans le nouveau F#. De manière critique - mes collègues seraient en mesure de comprendre cela lorsqu'ils regarderaient par-dessus mon épaule - et je pense qu'il est important d'impliquer davantage de personnes. Pour jouer à l'avocat du diable : div
ne prend pas longtemps à taper et si j'ai besoin de le découvrir, c'est le moindre des défis que je vais relever en utilisant une configuration qui fait de la magie tout en me cacher Javascript, React, CSS etc. etc. En général, cependant, la découvrabilité de l'API est excellente.
Particulièrement pour les transitions / animations sur les styles, la méthode Feliz a tellement de sens - vraiment agréable à travailler.
De manière plus générale et probablement tangentielle : le défi que j'ai constamment relevé consiste à comprendre où se termine le monde elmish et où commence le monde React. Ce n'est pas vraiment une préoccupation pour l'une ou l'autre bibliothèque - mais comme je n'ai la moindre idée de quoi que ce soit de React, le fait que je puisse obtenir un composant de fonction de Fable.React ou Feliz me confond. Bien sûr, apprendre ceci est ma responsabilité - mais j'ai pensé que cela valait la peine de le mentionner. Je mélange actuellement les approches Elmish et React et je pense que cela entraîne un nouveau rendu de tous les composants lors de la mise à jour - mais je ne connais pas la "bonne" façon de faire les choses pour éviter cela, car des exemples d'applications entièrement construites comme celle-ci sont difficile à trouver. Parfois, j'ai l'impression que je devrais d'abord apprendre à créer des applications React de qualité production en Javascript, puis passer à Fable/Elmish - mais cela semble être une barrière assez importante à l'entrée. Avis de non-responsabilité : je n'ai pas lu la documentation de Feliz en détail, et c'est vraiment bien - donc c'est probablement sur moi !
Merci à tous pour le partage de ces bibliothèques !
Merci pour votre contribution @drk-mtr !
Si je créais un élément personnalisé, je le ferais à la manière Fable.React pour le moment. J'ai jeté un coup d'œil à la construction de choses "à la manière de Feliz" et je me suis enfui instantanément (je reviendrai à un moment donné!).
Oui, cela peut sembler assez intimidant au début, mais c'est assez simple une fois que vous vous y êtes habitué. Cela étant dit, il s'agit d'un coût initial assez élevé pour le développeur lors de l'écriture de bibliothèques. Je trouve que c'est un compromis assez acceptable puisque la grande majorité des utilisateurs ne vont pas écrire des bibliothèques. Heureusement, le travail lui-même est assez simple, et un copier-coller peut accélérer une grande partie du travail (ou comme certains d'entre nous, écrire des générateurs pour créer le code pour nous).
Je trouverais vraiment utile que la documentation priorise plus régulièrement l'aspect "voici comment créer une propriété personnalisée". Je pense qu'il est juste d'accepter qu'avec seulement quelques auteurs, ces bibliothèques vont souvent avoir du mal à offrir une surface d'API pour tout ce qui est possible en CSS (par exemple), donc si un débutant se bloque alors qu'il ne peut pas définir "fontSize" dans "em", ou ne peuvent pas trouver "grid-template-rows", ou n'ont aucune idée qu'ils sont censés fournir un argument "Screen.All" - ce serait bien s'ils savaient comment le contourner.
Juste au cas où vous ne le sauriez pas (je ne saurais pas tout à fait dire à partir de ce contexte):
Il existe une méthode custom
sur les types prop
et style
.
Tout ce qui accepte ICssUnit
peut être défini comme : style.fontSize (length.em 1)
La mise à jour de la doc devrait être assez simple.
Je pense que certaines améliorations de la "qualité de vie" pourraient être apportées et commencer à réduire certains problèmes potentiels... par exemple, lorsque je regarde de longues listes verticales d'accessoires de style dans Feliz, je commence à me demander s'il pourrait y avoir un style.paddingHorizontal et style.paddingVertical. Je me rends compte que je pourrais créer mes propres aides, et ce n'est pas l'endroit pour faire des demandes de fonctionnalités cependant, et qu'il peut y avoir un désir d'éviter d'introduire des choses qui ne sont pas standard :)
Tout ce qui aide à rendre l'expérience utilisateur plus facile est définitivement le bienvenu dans mon livre. J'aimerais voir un problème/pr pour l'une de ces choses que vous avez remarquées !
De manière plus générale et probablement tangentielle : le défi que j'ai constamment relevé consiste à comprendre où se termine le monde elmish et où commence le monde React. Ce n'est pas vraiment une préoccupation pour l'une ou l'autre bibliothèque - mais comme je n'ai la moindre idée de quoi que ce soit de React, le fait que je puisse obtenir un composant de fonction de Fable.React ou Feliz me confond. Bien sûr, apprendre ceci est ma responsabilité - mais j'ai pensé que cela valait la peine de le mentionner. Je mélange actuellement les approches Elmish et React et je pense que cela entraîne un nouveau rendu de tous les composants lors de la mise à jour - mais je ne connais pas la "bonne" façon de faire les choses pour éviter cela, car des exemples d'applications entièrement construites comme celle-ci sont difficile à trouver. Parfois, j'ai l'impression que je devrais d'abord apprendre à créer des applications React de qualité production en Javascript, puis passer à Fable/Elmish - mais cela semble être une barrière assez importante à l'entrée. Avis de non-responsabilité : je n'ai pas lu la documentation de Feliz en détail, et c'est vraiment bien - donc c'est probablement sur moi !
Mon expérience dans le monde de Fable (avant Feliz) était que j'écrivais tout comme des fonctions normales pour la plupart (comme un composant pour chaque .fs). Le résultat a été que j'ai perdu beaucoup des avantages offerts par le système React. Depuis que j'ai commencé avec Feliz, j'ai dû vraiment me plonger dans le fonctionnement de React (une bonne chose imo). Depuis que je m'y suis habitué, je n'y pense même plus vraiment consciemment. Chaque fonction que j'écris et qui effectue le rendu est un composant de fonction.
Si vous voulez vraiment apprendre à écrire React de manière idiomatique, je pense que c'est aussi simple que de vous restreindre à ne pas utiliser Elmish et à n'utiliser que des composants/hooks de fonction dans votre application de démonstration. Vous pouvez assez facilement suivre tout ce qui est écrit en JS car l'API est presque exactement la même.
Avoir une application open source de qualité écrite en Feliz est certainement quelque chose qui serait bénéfique, j'ai créé un problème pour cela il y a quelque temps #67. La chose la plus proche en ce moment est probablement l'application docs elle-même, ou Electron Demo de @cmeeren .
Commentaire le plus utile
Salut @MangelMaxime ,
Tout d'abord, je tiens à vous remercier d'avoir pris le temps et les efforts non seulement d'essayer Feliz sur un projet, mais aussi d'écrire une analyse aussi approfondie. Je suis sûr que beaucoup d'utilisateurs de Fable le trouveront très utile et informatif.
J'apprécie vraiment ce genre de retour d'information et d'ouverture et je pense qu'il est crucial pour que cette bibliothèque soit la meilleure possible avec son écosystème :pray:
Je vais parler de Feliz.Bulma dans un instant parce que je pense que c'est un peu spécial dans l'écosystème, d'abord je vais répondre à certains problèmes que vous avez qui peuvent déjà être corrigés sans changements dans la bibliothèque.
Surcharges de
onChange
En fait, vous ne le faites pas. Le type doit être déduit de votre type de message comme suit :
Selon le type de
ChangeGistToken
, la surcharge correcte sera déduite. Lorsque le cas DU estChangeGistToken of string
alors la surcharge destring -> unit
sera choisie. Mais cela peut aussi êtreFileSelected of File
lorsque le type d'entrée estfile
auquel cas la surchargeFile -> unit
sera déduite et ainsi de suite. Vous n'avez pas besoin du sucre de syntaxe deev.Value
deFable.React
Rappels multi-arguments
@Shmew a déjà abordé le problème et c'est en effet le moyen de résoudre ces types de propriétés. Il est crucial de comprendre que lors de l'implémentation des extensions Feliz, vous pouvez effectuer des transformations avant de définir la propriété :
En suivant cette logique, vous pouvez convertir
'A -> 'B -> unit
enFunc<'A, 'B, unit>
interne :Il s'agit d'une version plus explicite que celle fournie par @Shmew (Comme vous le savez avec les fixations, vous devez toujours jouer un peu avec elles pour les obtenir correctement). Un exemple fonctionnel de ceci est ici
Type-Safety dans l'écosystème Feliz
Comme @Shmew mentionné ci-dessus : une extension Feliz appropriée ne vous permet pas d'écrire du code qui se comporte de manière incorrecte et la contrainte des types de propriétés vous permet de l'implémenter. Vous pouvez soit créer des propriétés qui renvoient
IReactProperty
comme le fait Feliz.Recharts qui permet une compatibilité descendante avec les propriétés existantes dansprop
ou vous pouvez implémenter un type plus spécialiséISpecialiedProperty
pour prendre en charge seulement un sous-ensemble des propriétés tout en dupliquant les noms de certaines propriétés à partir deIReactProperty
pour assurer la rétrocompatibilité :C'est ce que j'ai fait avec Feliz.AntDesign, par exemple ici où je duplique certaines propriétés pour les rendre disponibles à partir du point d'entrée
button
(plus tard, je pourrais rendre les propriétés plus spécialisées avec des types stricts mais au début , la compatibilité descendante est l'endroit où vous commencez pour la première fois). Je crois que Feliz.MaterialUI fait des choses similaires pour étendreIReactProperty
plutôt que de spécialiser les propriétés. Cela nous amène au cas de Feliz.Bulma.Le cas de Feliz.Bulma
Avant l'implémentation de Feliz.Bulma, j'avais en tête l'idée qu'il n'était pas nécessaire de créer des extensions Feliz pour les frameworks CSS uniquement. Feliz facilite déjà la composition de plusieurs noms de classes et leur utilisation conditionnelle. Maintenant, avec les
yield
implicites en F#, c'est encore plus facile. Combinez cela avec TypedCssClasses et vous supprimez le besoin d'implémenter un framework CSS uniquement. Coûts de mise en œuvre et de maintenance nuls. Utilisez simplement la feuille de style dactylographiée et utilisez la très bonne documentation du projet lui-même au lieu d'avoir à créer vous-même un site Web de documentation.Cependant, après avoir vu l'implémentation de Feliz.Bulma par @Dzoukr, cela avait du sens : vous pourriez simplifier beaucoup le code en introduisant des "points d'entrée" spécifiques aux composants CSS d'un module de Bulma afin que vous obteniez
Bulma.button
,Bulma.card
etc. Ils sont vraiment agréables à utiliser. En ce qui concerne les modificateurs, je pense que l'approche de Roman est facile à suivre et à utiliser mais comme vous l'avez dit, cela peut parfois donner des résultats intéressants. Il y a sûrement plus à améliorer dans ce département et en tout cas, je pense que Roman a fait un excellent travail en suivant la vision de Feliz en créant quelque chose de facile à apprendre et à utiliser.Gestion des packages de Feliz
Je veux juste souligner une énorme différence entre les
Fable.React
et lesFeliz
actuels : le versioning et la gestion des packages. Les versions de Feliz sont toutes ses bibliothèques d'écosystèmes compatibles Femto, ce qui évite aux utilisateurs d'avoir à installer manuellement les packages npm requis ou de savoir avec quelle version ils sont compatibles. Ceci est différent de beaucoup sinon de toutes les bibliothèques dérivéesFable.React
et mêmeFable.React
lui-même où ce n'est pas le cas.Plans futurs