Feliz: guide tiers

Créé le 11 août 2019  ·  25Commentaires  ·  Source: Zaid-Ajaj/Feliz

Cela semble fantastique à mon humble avis.

Est-ce simple à étendre pour des éléments tiers, tels que Material-UI ?

Ce serait formidable d'avoir une sorte de guide sur la façon de le faire. :)

Commentaire le plus utile

Bonjour @cmeeren , je suppose que nous pouvons considérer cela comme résolu, n'est-ce pas ? Je vais fermer le sujet, veuillez le rouvrir pour une discussion plus approfondie si nécessaire

Tous les 25 commentaires

Cela semble fantastique à mon humble avis.

Merci! Contente que ça te plaise :sourire:

Est-ce simple à étendre pour des éléments tiers, tels que Material-UI ?

Bien sûr, mais l'approche est un peu différente parce que nous n'utilisons pas de syndicats discriminés. J'essaie d'écrire quelque chose quand le temps le permet, mais l'idée est que vous avez essentiellement deux options :

  • 1) Étendre le type statique prop avec plus de fonctions
  • 2) Créez un type statique prop similaire, spécifique à votre bibliothèque (peut-être Mui ) qui possède toutes les propriétés requises par le Mui. Vous pouvez même alias prop avec Mui et étendre Mui avec des propriétés et des fonctions spécifiques à Mui (surchargées).

L'extension est aussi simple que :

type prop with
  static member hello (value: string) = Interop.mkAttr "hello" value 

Que ce soit la "forme finale" de la bibliothèque et la manière de l'étendre, est toujours à l'étude car je veux d'abord l'essayer en construisant des bibliothèques tierces et voir comment ça se passe

Merci! Je suis déjà en train de tester quelque chose pour MUI, juste pour une fraction de l'API, pour voir comment cela fonctionne. Je partagerai probablement au cours du week-end/fin, ce serait formidable si vous pouviez y jeter un coup d'œil juste pour voir si je suis sur la bonne voie et si je suis l'esprit de Feliz. Ça a l'air d'être un succès jusqu'à présent !

Cependant, je n'ai pas étendu le type prop existant. J'ai défini un type prop séparé dans un autre espace de noms ( Feliz.MaterialUI ). Fonctionne apparemment très bien ; vous avez bien sûr accès à tous les membres de tous les types correspondants si vous ouvrez à la fois Feliz et Feliz.MaterialUI .

J'ai un type Mui qui correspond à Html et contient les composants réels.

(Actuellement, j'ai placé des accessoires spécifiques aux composants dans des sous-modules séparés de prop , comme mentionné au #13.)

Un point d'amélioration possible à Feliz est d'avoir un reactElement et createElement qui n'acceptent pas string , mais ReactElementType (je pense). afin que nous puissions appeler createElement (importDefault "@material-ui/core/Button") . J'ai créé ces deux assistants moi-même actuellement.

Au fait, tous les membres devraient-ils être inline ? Quels sont les avantages/inconvénients ? J'ai remarqué que vous n'avez pas utilisé inline ci-dessus, mais tout à Feliz est inline .

Merci! Je suis déjà en train de tester quelque chose pour MUI, juste pour une fraction de l'API, pour voir comment cela fonctionne. Je partagerai probablement au cours du week-end/fin, ce serait formidable si vous pouviez y jeter un coup d'œil juste pour voir si je suis sur la bonne voie et si je suis l'esprit de Feliz. Ça a l'air d'être un succès jusqu'à présent !

C'est génial! Je serais certainement heureux de jeter un coup d'œil si vous

Cependant, je n'ai pas étendu le type d'accessoire existant. J'ai défini un type d'accessoire séparé dans un autre espace de noms (Feliz.MaterialUI). Fonctionne apparemment très bien ; vous avez bien sûr accès à tous les membres de tous les types correspondants si vous ouvrez à la fois Feliz et Feliz.MaterialUI.

J'ai un type Mui qui correspond à Html et contient les composants réels.

C'est ce que je ferais pour Mui

(Actuellement, j'ai placé des accessoires spécifiques aux composants dans des sous-modules séparés de l'accessoire, comme mentionné au n ° 13.)

Cela a du sens pour Mui en raison du nombre d'options que vous avez

Un point d'amélioration possible dans Feliz est d'avoir un reactElement et un createElement qui n'acceptent pas de chaîne, mais ReactElementType (je pense). afin que nous puissions appeler createElement (importDefault "@material-ui/core/Button"). J'ai créé ces deux assistants moi-même actuellement.

Je passe en revue les membres que j'utilise dans le module Interop , je viens d'exposer tout ce que j'utilisais dans la bibliothèque, sera reconsidéré pour la version stable

Au fait, tous les membres devraient-ils être en ligne ? Quels sont les avantages/inconvénients ? Je remarque que vous n'avez pas utilisé inline ci-dessus, mais tout dans Feliz est en ligne.

J'aurais dû insérer le membre étendu ci-dessus !

La règle d'or est la suivante : si la valeur de la propriété est primitive comme une chaîne/int/bool/enum, inlinez la propriété, mais si votre propriété calcule une valeur basée sur l'entrée, il est préférable de ne pas l'inline car chaque fois que l'utilisateur appelle la fonction en ligne, tout le corps de la fonction est en ligne à cet endroit d'invocation, donc si un utilisateur utilise la même fonction 10 fois dans l'application, le corps de la fonction est compilé en ligne 10 fois au lieu d'être défini une fois et référencé 10 fois

La règle d'or est la suivante : si la valeur de la propriété est primitive comme une chaîne/int/bool/enum, inlinez la propriété, mais si votre propriété calcule une valeur basée sur l'entrée, il est préférable de ne pas l'inline car chaque fois que l'utilisateur appelle la fonction en ligne, tout le corps de la fonction est en ligne à cet endroit d'invocation, donc si un utilisateur utilise la même fonction 10 fois dans l'application, le corps de la fonction est compilé en ligne 10 fois au lieu d'être défini une fois et référencé 10 fois

Bon à savoir! Mais (dans le contexte de Fable) pourquoi inline en premier lieu, alors ? Quel est l'avantage pour les corps de fonction/méthode "simples" ?

  • Taille du bundle : si ces fonctions ne sont pas utilisées (et il y en a des centaines), elles sont quand même compilées, ce qui gonfle la taille du bundle AFAIK (l'impact est bien sûr toujours relatif à la taille totale de l'application)
  • Arguments de type générique : non pertinents pour Feliz mais dans le contexte de Fable, une fonction générique avec des arguments de type générique ne se compilera pas si le lieu d'invocation n'est pas en ligne (tous les arguments de type générique doivent être résolus statiquement au moment de la compilation) sauf cas particuliers où vous pouvez utiliser [<Inject>] ITypeResolver<'t> comme argument facultatif d'une classe statique (seules les bibliothèques hautement spécialisées utilisent cette fonctionnalité, voir Fable.SimpleJson/Thoth.Json)

Je pense que babel secoue les arbres et supprime les fonctions inutilisées lorsque vous créez le bundle de production. L'inlining irait à l'encontre de cela.

@Luiz-Monad Êtes-vous en train de dire que, idéalement, rien à Feliz ne devrait être aligné? Que l'inlining pour des raisons de taille de bundle est contre-productif ?

@Luiz-Monad Ce que vous dites serait génial ! Du moins si la compilation fonctionnait de cette façon. Voici un exemple que vous pouvez essayer avec le REPL :

module App

type prop = 
  // does useless stuff
  static member f() = 
    [ 1 .. 100 ]
    |> List.map (fun x -> x * 20)
    |> List.collect (fun n -> [n; n])
    |> List.fold (+) 0

  // does useless stuff
  static member inline k() = 
    [ 1 .. 100 ]
    |> List.map (fun x -> x * 20)
    |> List.collect (fun n -> [n; n])
    |> List.fold (+) 0

  static member g() = 1

let value = prop.g()

printfn "%d" value

prop contient :

  • f() contient un corps de fonction -> non aligné et également non utilisé
  • k() contient un corps de fonction -> inline mais non utilisé
  • g() contient une fonction -> pas en ligne et utilisée

On pourrait penser que f() et g() ne seront pas compilés, mais ce n'est pas le cas, f() (non intégré et non utilisé) est compilé de toute façon, mais k() (inlined, not used) n'entre pas dans le bundle compilé

import { fold, collect, map, ofSeq, ofArray } from "fable-library/List.js";
import { type } from "fable-library/Reflection.js";
import { rangeNumber } from "fable-library/Seq.js";
import { toConsole, printf } from "fable-library/String.js";
import { declare } from "fable-library/Types.js";
export const prop = declare(function App_prop() {});
export function prop$reflection() {
  return type("App.prop");
}
export function prop$$$f() {
  return fold(function folder(x$$1, y) {
    return x$$1 + y;
  }, 0, collect(function mapping$$1(n) {
    return ofArray([n, n]);
  }, map(function mapping(x) {
    return x * 20;
  }, ofSeq(rangeNumber(1, 1, 100)))));
}
export function prop$$$g() {
  return 1;
}
export const value = prop$$$g();
toConsole(printf("%d"))(value);

Cela fonctionne en effet, mais vous devez configurer webpack pour ce faire, car ce qui fait trembler l'arbre n'est pas une fable en soi, donc cela ne fonctionnera pas dans le REPL.

Avant de

/// Library.fs
module Library

type prop = 
    // does useless stuff
    static member f() = 
      [ 1 .. 100 ]
      |> List.map (fun x -> x * 20)
      |> List.collect (fun n -> [n; n])
      |> List.fold (+) 0

    // does useless stuff
    static member inline k() = 
      [ 1 .. 100 ]
      |> List.map (fun x -> x * 20)
      |> List.collect (fun n -> [n; n])
      |> List.fold (+) 0


type AppMain =
    static member g() = 1

//// App.fs
module App

let value = Library.AppMain.g ()

printfn "%d" value

Après

  declare(function Library_prop() { 
     // see its empty, this weren't removed too because of `keep_classnames: true, keep_fnames: true ` in the terser plugin
  });
  declare(function Library_AppMain() {
  });
  !function toConsole(arg) {
    return arg.cont(function (x) {
      console.log(x)
    })
  }(printf('%d')) (1),
  __webpack_require__.d(__webpack_exports__, 'value', function () {
    return 1
  })

De plus, il y a une mise en garde à votre test, le fichier d'entrée est un peu spécial en ce sens que les fonctions qu'il contient ne sont pas supprimées. (Je suppose qu'il y a quelque chose à voir avec l'initialisation des classes statiques, quelque chose doit appeler le constructeur d'initialisation statique pour faire des effets secondaires sur les modules)

Voir ce référentiel que j'ai créé pour ce test https://github.com/Luiz-Monad/test-tree-shaking

Merci beaucoup pour l'exemple de dépôt ! Je vais certainement étudier cela plus avant pour voir si l'inlining fait réellement quelque chose d'utile dans le contexte de Feliz

Je vais certainement étudier cela plus avant pour voir si l'inlining fait réellement quelque chose d'utile dans le contexte de Feliz

Cool, hâte d'entendre ce que tu trouveras :)

C'est génial! Je serais certainement heureux de jeter un coup d'œil si vous

Veuillez consulter la branche feliz sur cmeeren/fable-elmish-electron-material-ui-demo .

La plupart du code est généré automatiquement sur la base de la documentation de l'API (HTML). Il y a un projet de générateur (moche et hacky, mais fait le travail) et un projet pour les liaisons réelles. Dans le projet de rendu, seul App.fs a été converti pour utiliser les nouvelles liaisons de style Feliz.

Jetez-y un coup d'œil quand vous en avez envie, et dites-moi ce que vous en pensez et si vous avez des questions.

@cmeeren A l'air très agréable et bien meilleur que l'API actuelle IMHO mais est-ce encore un peu difficile à lire mais c'est plus la nature de la bibliothèque elle-même, elle a beaucoup de parties très spécifiques avec lesquelles vous devez vous familiariser. Je pense qu'il y a des parties qui pourraient être améliorées, prenons cet exemple :

Mui.appBar [
  prop.className c.appBar
  prop.appBar.position.fixed'
  prop.children [
    Mui.toolbar [
      prop.children [
        Mui.typography [
          prop.typography.variant.h6
          prop.typography.color.inherit'
          prop.text (pageTitle model.Page)
        ]
      ]
    ]
  ]
]

Ma version personnelle parfaite de cet extrait serait de le transformer en ce qui suit :

Mui.appBar [
  AppBar.className c.appBar
  AppBar.position.fixed'
  AppBar.children [
    Mui.toolbar [
      Toolbar.children [
        Mui.typography [
          Typography.variant.h6
          Typography.color.inherit'
          Typygraphy.text (pageTitle model.Page)
        ]
      ]
    ]
  ]
]

De cette façon, il est facile de trouver des éléments Mui car vous pouvez simplement "pointer" Mui et une fois que vous avez trouvé votre élément ( appBar ), vous pouvez "pointer" le nom du module ( AppBar ) pour définir les propriétés et telles

Peut-être garder AppBar en minuscules aussi

Je pense que vous avez compris l'idée mais pour être plus exact, la syntaxe générale de cette API est la suivante où {Element} est un élément react :

Mui.{element} [
  {Element}.{propName}.{propValue}
  {Element}.children [
    Mui.{otherElem} [
      {OtherElem}.{propName}.{propValue}
      // etc.
    ]
  ]
]

Que penses-tu de cela? Cette API a également inspiré à la bibliothèque les idées de fabuleux-simples-éléments si vous voulez voir une implémentation concrète

Je pense que c'est parfait, et juste le genre de chose sur laquelle je voulais avoir votre avis. J'ai initialement choisi d'avoir tout sous prop car c'est ainsi que fonctionnait la bibliothèque principale, mais bien sûr, vous n'avez pas vraiment d'accessoires spécifiques aux composants, alors que MUI a plus plus moins seulement des accessoires spécifiques aux composants.

Je pense que s'en tenir aux noms de module en minuscules peut sembler préférable (et économiser une frappe supplémentaire), mais je suis ouvert aux contre-arguments.

C'est bien que j'aie généré automatiquement ce truc, ce qui le rend simple à changer.

Je mettrai à jour et je vous tiendrai au courant.

Il y a une chose en particulier sur laquelle j'aimerais avoir des avis. C'est le truc ClassName . Le crochet makeStyles renvoie un objet avec les mêmes accessoires que celui dans lequel il est passé, mais où chaque élément (JSS) a été remplacé par une chaîne, qui est le nom de la classe à utiliser (et peut être passé à par exemple prop.className ).

Maintenant, il n'y a aucun moyen de taper cela en F #, donc je dois travailler avec ce que j'ai. Normalement, tous les props de l'objet style sont IStyleAttribute list . Cela signifie que je peux ajouter une surcharge pour prop.className qui accepte IStyleAttribute list , ce qui est bien sûr un mensonge car au moment de l'exécution, c'est une chaîne. Si vous réussissiez réellement IStyleAttribute list , cela échouerait. En plus de prop.className , cela vaut également pour tous les classes.<element>.<classesName> (utilisés dans <element>.classes [ ... ] ). Ils acceptent également un nom de classe (chaîne).

Ce que j'ai fait, comme vous pouvez le voir, est "d'exiger" que toutes les propriétés IStyleAttribute list de l'objet de style soient enveloppées dans asClassName , qui se déballe simplement en IClassName (un proxy pour string , si vous le souhaitez). Et puis j'ai ajouté une surcharge à prop.className acceptant IClassName , et fait en sorte que tous les accessoires classes acceptent IClassName . J'aime le fait qu'il soit plus fortement typé, mais je n'aime pas le fait qu'il nécessite une saisie supplémentaire ( asClassName pour chaque règle CSS de niveau supérieur). Le compilateur se plaindra si vous le manquez, mais il ne vous dira pas quoi faire, et c'est toujours du bruit supplémentaire.

Avez-vous des commentaires à ce sujet?

Aussi, j'ai remarqué ceci :

f# listItem.divider ((page = Home))

Ici, les doubles parenthèses sont nécessaires, car sinon F# l'interprète comme essayant d'appeler listItem.divider avec le paramètre (inexistant) page défini sur Home (au lieu de value paramètre page = Home ). Voyez-vous un moyen d'éviter cela?

Bonjour @cmeeren , tout d'abord, j'adore cette syntaxe :

Mui.appBar [
  prop.className c.appBar
  appBar.position.fixed'
  appBar.children [
    Mui.toolbar [
      toolbar.children [
        Mui.typography [
          typography.variant.h6
          typography.color.inherit'
          prop.text (pageTitle model.Page)
        ]
      ]
    ]
  ]
]

Ça a l'air si propre et si simple! Bien que si j'étais vous, j'aurais probablement dupliqué certaines des fonctions génériques prop dans des accessoires spécifiques aux composants tels que appBar.className au lieu de (ou à côté) prop.className afin que ils ont tous l'air symétriques mais surtout pour donner la surcharge IClassName au composant spécifique à Mui au lieu du générique prop.className qui prend une chaîne car makeStyles est également un Mui-spécifique construct et il est logique qu'il ne s'applique qu'aux composants Mui.

Je pense que vous avez abordé les makeStyles la meilleure façon possible, du moins pour le moment, je ne peux pas penser à une meilleure façon (bien que je ne sois pas un grand fan de asClassName , peut-être Styles.createClass la place ? c'est à vous de décider).

Quant à listItem.divider ((page = Home)) , c'est délicat, vous pouvez ajouter une fonction factice let when (x: bool) = x mais ce n'est que du bruit. Je pense qu'il est préférable de le classer en tant que bogue du compilateur car je ne peux pas penser à la raison pour laquelle le compilateur F # ne peut pas résoudre la surcharge appropriée de la fonction, je n'ai pas essayé moi-même mais je l'examinerai quand le temps le permettra

Enfin, je ne suis pas aussi réactif que d'habitude cette semaine car je suis en vacances maintenant, donc je ne pourrai peut-être pas tout lire/vérifier, etc.

Bien que si j'étais vous, j'aurais probablement dupliqué certaines des fonctions génériques prop dans des accessoires spécifiques aux composants tels que appBar.className au lieu de (ou à côté) prop.className afin que ils ont tous l'air symétriques mais surtout pour donner la surcharge IClassName au composant spécifique à Mui au lieu du générique prop.className qui prend une chaîne car makeStyles est également un Mui-spécifique construct et il est logique qu'il ne s'applique qu'aux composants Mui.

Découvrez-le maintenant :) J'ai apporté des améliorations et des extensions drastiques ces derniers jours, je les ai juste poussées (pas fait, je suis actuellement en train de parcourir tous les composants MUI pour vérifier et améliorer les accessoires générés).

Je pense que vous avez abordé les makeStyles la meilleure façon possible, du moins pour le moment, je ne peux pas penser à une meilleure façon (Bien que je ne sois pas un grand fan de asClassName , peut-être Styles.createClass la place ? c'est à vous de décider).

J'aimerais le garder aussi court que possible car il sera beaucoup utilisé, mais je suis ouvert à d'autres noms. Bien que j'aie à moitié envie d'avoir juste une surcharge IStyleAttribute list . Cela supprimera potentiellement pas mal de bruit, et je doute que ce soit très dangereux même s'il peut techniquement être utilisé de manière incorrecte.

Quant à listItem.divider ((page = Home)) , c'est délicat, vous pouvez ajouter une fonction factice let when (x: bool) = x mais ce n'est que du bruit. Je pense qu'il est préférable de le classer en tant que bogue du compilateur car je ne peux pas penser à la raison pour laquelle le compilateur F # ne peut pas résoudre la surcharge appropriée de la fonction, je n'ai pas essayé moi-même mais je l'examinerai quand le temps le permettra

Merci, j'ai déposé un problème maintenant : https://github.com/dotnet/fsharp/issues/7423

Enfin, je ne suis pas aussi réactif que d'habitude cette semaine car je suis en vacances maintenant, donc je ne pourrai peut-être pas tout lire/vérifier, etc.

Compris, pas de problème. Je continuerai à publier des problèmes si je rencontre des problèmes, et vous répondez simplement à votre rythme.

Découvrez-le maintenant :) J'ai apporté des améliorations et des extensions drastiques ces derniers jours, je les ai juste poussées (pas fait, je suis actuellement en train de parcourir tous les composants MUI pour vérifier et améliorer les accessoires générés).

Ça a l'air vraiment bien, avec les documents générés aussi 😍 peut-être qu'il est temps de mettre son propre dépôt ?

J'aimerais le garder aussi court que possible car il sera beaucoup utilisé, mais je suis ouvert à d'autres noms. Bien que j'aie à moitié envie d'avoir juste une surcharge de liste IStyleAttribute. Cela supprimera potentiellement pas mal de bruit, et je doute que ce soit très dangereux même s'il peut techniquement être utilisé de manière incorrecte.

Cela fonctionnerait aussi, les bibliothèques Fable trompent le système de type tout le temps ;)

Merci, j'ai déposé un problème maintenant : dotnet/fsharp#7423

Impressionnant! Merci beaucoup

Ça a l'air vraiment bien, avec les documents générés aussi 😍 peut-être qu'il est temps de mettre son propre dépôt ?

J'y ai pensé, mais il y a encore quelques bugs importants (par exemple # 27) que je préférerais comprendre avec la commodité d'avoir tout au même endroit, donc je pense que je vais le garder là jusqu'à ce qu'il soit prêt pour une avant-première sur nuget (espérons-le ne sera pas trop longue).

@Zaid-Ajaj J'ai presque fini avec Feliz.MaterialUI. Publiera bientôt dans un dépôt séparé. Ce serait formidable que vous le révisiez, d'abord et avant tout pour vérifier certaines décisions de conception et assurer la cohérence avec Feliz, mais aussi pour vérifier certains éléments d'implémentation (par exemple, est-ce que j'utilise des choses que vous ferez en interne, ou y a-t-il des choses que je Je n'utilise pas Feliz mais je devrais utiliser).

Lorsque je crée le nouveau dépôt, est-ce que je peux créer des problèmes expliquant ce que j'aimerais revoir et vous taguer ?

J'ai maintenant téléchargé un brouillon de Feliz.MaterialUI sur cmeeren/Feliz.MaterialUI . J'ai créé plusieurs problèmes avec des choses que j'aimerais revoir.

Je vous serais très reconnaissant si vous pouviez trouver le temps d'y jeter un œil !

Il n'est pas nécessaire de passer beaucoup de temps sur chaque problème ; Je veux vraiment juste un deuxième avis pour les raisons mentionnées dans mon commentaire précédent. Je suis heureux d'aller dans les mauvaises herbes si vous voulez, mais même juste "ça a l'air bien" serait génial.

Il n'y a pas d'urgence, bien sûr. :)

Super travail @cmeeren ! A première vue, les reliures ont l'air très propres, je vais jeter un oeil à chaque numéro dans les prochains jours, promis :sourire:

Hé! Une chance de continuer à examiner les problèmes ? Encore une fois pas pressé, juste un rappel amical puisque je n'ai pas eu de vos nouvelles depuis quelques semaines 😃

En fait, j'ai examiné les problèmes, mais comme je l'ai déjà dit, qu'une API soit bonne ou non provient de cas d'utilisation, c'est pourquoi je vous ai demandé de publier une version alpha afin que je puisse l'essayer, mais je n'ai pas eu le il est encore temps de le faire (c'était dans ma tête cette semaine :sourire:)

Bonjour @cmeeren , je suppose que nous pouvons considérer cela comme résolu, n'est-ce pas ? Je vais fermer le sujet, veuillez le rouvrir pour une discussion plus approfondie si nécessaire

Cette page vous a été utile?
0 / 5 - 0 notes