Feliz: guía de terceros

Creado en 11 ago. 2019  ·  25Comentarios  ·  Fuente: Zaid-Ajaj/Feliz

Esto se ve fantástico en mi humilde opinión.

¿Es esto fácil de extender para cosas de terceros, como Material-UI ?

Sería genial tener algún tipo de guía sobre cómo hacer eso. :)

Comentario más útil

Hola @cmeeren , supongo que podemos considerar esto resuelto, ¿verdad? Cerraré el problema, vuelva a abrirlo para una mayor discusión si es necesario

Todos 25 comentarios

Esto se ve fantástico en mi humilde opinión.

¡Gracias! Me alegra que te guste :sonrisa:

¿Es esto fácil de extender para cosas de terceros, como Material-UI?

Claro, pero el enfoque es un poco diferente porque no estamos usando sindicatos discriminados. Intento escribir algo cuando el tiempo lo permite, pero la idea es que básicamente tienes dos opciones:

  • 1) Ampliar el tipo estático prop con más funciones
  • 2) Cree un tipo estático prop similar, específico para su biblioteca (tal vez Mui ) que tenga todas las propiedades que requiere Mui. Incluso podría alias prop con Mui y ampliar Mui con propiedades y funciones específicas de Mui (sobrecargadas).

Extender es tan simple como:

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

Aún se está considerando si esta es la "forma final" de la biblioteca y la forma de ampliarla, ya que primero quiero probarla creando bibliotecas de terceros y ver cómo funciona.

¡Gracias! Ya estoy bastante avanzado probando algo para MUI, solo por una fracción de la API, para ver cómo funciona. Probablemente lo compartiré en algún momento durante el fin de semana, sería genial si pudiera echarle un vistazo solo para ver si estoy en el buen camino y sigo el espíritu Feliz. ¡Parece un éxito hasta ahora!

Sin embargo, no he extendido el tipo prop existente. He definido un tipo prop separado en otro espacio de nombres ( Feliz.MaterialUI ). Funciona aparentemente genial; por supuesto, obtienes acceso a todos los miembros de todos los tipos coincidentes si abres Feliz y Feliz.MaterialUI .

Tengo un tipo Mui que corresponde a Html y contiene los componentes reales.

(Actualmente he colocado accesorios específicos de componentes en submódulos separados de prop , como se menciona en el n.° 13).

Un posible punto de mejora en Feliz es tener reactElement y createElement que no acepten string , sino ReactElementType (creo). para que podamos llamar a createElement (importDefault "@material-ui/core/Button") . Yo mismo he creado estos dos ayudantes actualmente.

Por cierto, ¿todos los miembros deberían ser inline ? ¿Cuáles son los pros/contras? Noté que no usaste inline arriba, pero todo en Feliz es inline .

¡Gracias! Ya estoy bastante avanzado probando algo para MUI, solo por una fracción de la API, para ver cómo funciona. Probablemente lo compartiré en algún momento durante el fin de semana, sería genial si pudiera echarle un vistazo solo para ver si estoy en el buen camino y sigo el espíritu Feliz. ¡Parece un éxito hasta ahora!

¡Eso es genial! Definitivamente estaría feliz de echar un vistazo si usted

Sin embargo, no he ampliado el tipo de apoyo existente. He definido un tipo de accesorio separado en otro espacio de nombres (Feliz.MaterialUI). Funciona aparentemente genial; por supuesto, obtienes acceso a todos los miembros de todos los tipos coincidentes si abres tanto Feliz como Feliz.MaterialUI.

Tengo un tipo Mui que corresponde a Html y contiene los componentes reales.

Eso es lo que haría por Mui

(Actualmente, he colocado accesorios específicos de componentes en submódulos separados de accesorios, como se menciona en el n. ° 13).

Tiene sentido para Mui debido a la cantidad de opciones que tiene.

Un posible punto de mejora en Feliz es tener un elemento reactElement y createElement que no acepte cadenas, sino ReactElementType (creo). para que podamos llamar a createElement (importDefault "@material-ui/core/Button"). Yo mismo he creado estos dos ayudantes actualmente.

Estoy revisando los miembros que estoy usando en el módulo Interop , acabo de exponer todo lo que estaba usando en la biblioteca, se reconsiderará para la versión estable

Por cierto, ¿deberían estar todos los miembros en línea? ¿Cuáles son los pros/contras? Noté que no usaste en línea arriba, pero todo en Feliz está en línea.

¡Debería haber alineado el miembro extendido arriba!

La regla general es: si el valor de la propiedad es primitivo como una cadena/int/bool/enum, inserte la propiedad en línea, pero si su propiedad calcula un valor basado en la entrada, entonces es mejor no en línea porque cada vez que el usuario llama a la función en línea, todo el cuerpo de la función está en línea en ese lugar de invocación, por lo que si un usuario usa la misma función 10 veces en toda la aplicación, el cuerpo de la función se compila en línea 10 veces en lugar de definirse una vez y referenciarse 10 veces

La regla general es: si el valor de la propiedad es primitivo como una cadena/int/bool/enum, inserte la propiedad en línea, pero si su propiedad calcula un valor basado en la entrada, entonces es mejor no en línea porque cada vez que el usuario llama a la función en línea, todo el cuerpo de la función está en línea en ese lugar de invocación, por lo que si un usuario usa la misma función 10 veces en toda la aplicación, el cuerpo de la función se compila en línea 10 veces en lugar de definirse una vez y referenciarse 10 veces

¡Bueno saber! Pero (en el contexto de Fable) ¿por qué en línea en primer lugar, entonces? ¿Cuál es el beneficio para los cuerpos de métodos/funciones "simples"?

  • Tamaño del paquete: si estas funciones no se usan (y hay cientos de ellas), aún se compilan de todos modos, inflando el tamaño del paquete AFAIK (el impacto, por supuesto, sigue siendo relativo al tamaño total de la aplicación)
  • Argumentos de tipo genérico: irrelevantes para Feliz pero en el contexto de Fable, una función genérica con argumentos de tipo genérico no se compilará si el lugar de invocación no está en línea (todos los argumentos de tipo genérico deben resolverse estáticamente en tiempo de compilación) excepto en casos especiales donde puede usar [<Inject>] ITypeResolver<'t> como un argumento opcional de una clase estática (solo las bibliotecas altamente especializadas usan esta función, consulte Fable.SimpleJson/Thoth.Json)

Creo que babel sacude los árboles y elimina las funciones no utilizadas cuando creas el paquete de producción. Inlining derrotaría eso.

@Luiz-Monad ¿Entonces estás diciendo que, idealmente, nada en Feliz debería estar en línea? ¿Que la incorporación por razones de tamaño de paquete es contraproducente?

@Luiz-Monad ¡Lo que estás diciendo sería increíble! Al menos si la compilación funcionara de esa manera. Aquí hay un ejemplo que puede probar con el 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

Donde prop contiene:

  • f() contiene un cuerpo de función -> no está en línea y tampoco se usa
  • k() contiene un cuerpo de función -> en línea pero no se usa
  • g() contiene una función -> no en línea y utilizada

Podría pensar que tanto f() como g() no se compilarán, pero ese no es el caso, f() (no en línea y no se usa) se compila de todos modos, pero k() (en línea, no se usa) no se incluye en el paquete compilado

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

De hecho, funciona, pero debe configurar webpack para hacerlo, porque lo que hace el movimiento del árbol no es una fábula en sí misma, por lo que no funcionará en el REPL.

Antes

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

Despué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
  })

Además, hay una advertencia para su prueba, el archivo de entrada es algo especial en el sentido de que no se eliminan las funciones. (Supongo que hay algo que ver con la inicialización de clases estáticas, algo tiene que llamar al constructor del inicializador estático para hacer efectos secundarios en los módulos)

Vea este repositorio que hice para esta prueba https://github.com/Luiz-Monad/test-tree-shaking

¡Muchas gracias por el repositorio de ejemplo! Definitivamente investigaré esto más a fondo para ver si la inserción realmente está haciendo algo útil en el contexto de Feliz

Definitivamente investigaré esto más a fondo para ver si la inserción realmente está haciendo algo útil en el contexto de Feliz

Genial, deseando escuchar lo que encuentres :)

¡Eso es genial! Definitivamente estaría feliz de echar un vistazo si usted

Consulte la sucursal feliz en cmeeren/fable-elmish-electron-material-ui-demo .

La mayor parte del código se genera automáticamente en función de los documentos de la API (HTML). Hay un proyecto de generador (feo y raro, pero hace el trabajo) y un proyecto para los enlaces reales. En el proyecto del renderizador, solo App.fs se ha convertido para usar los nuevos enlaces de estilo Feliz.

Por favor, échale un vistazo cuando te apetezca y déjame saber lo que piensas y si tienes alguna pregunta.

@cmeeren Se ve muy bien y mucho mejor que la API actual en mi humilde opinión, pero todavía es un poco difícil de leer, pero esa es más la naturaleza de la biblioteca en sí, tiene muchas partes muy específicas con las que debe familiarizarse. Creo que hay partes que podrían mejorarse, tome este ejemplo:

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

Mi versión personal perfecta de este fragmento sería convertirlo en lo siguiente:

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 esta forma, es fácil encontrar elementos de Mui porque simplemente puede "puntear" Mui y una vez que haya encontrado su elemento ( appBar ), puede "puntear" el nombre del módulo ( AppBar ) para definir las propiedades y tal

Tal vez mantenga AppBar en minúsculas también

Creo que entiendes la idea, pero para ser más exactos, la sintaxis general de esta API es la siguiente, donde {Element} es un elemento de reacción:

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

¿Qué piensas sobre esto? Esta API también inspiró a la biblioteca las ideas de elementos fabulosos y simples si desea ver una implementación concreta.

Creo que es perfecto, y justo el tipo de cosas sobre las que quería saber tu opinión. Inicialmente elegí tener todo por debajo prop ya que así es como funcionaba la biblioteca principal, pero por supuesto, en realidad no tienes accesorios específicos de componentes, mientras que MUI tiene más accesorios específicos de componentes.

Creo que apegarse a los nombres de los módulos en minúsculas puede verse mejor (y ahorrar una pulsación de tecla adicional), pero estoy abierto a contraargumentos.

Es bueno que haya generado automáticamente estas cosas, eso hace que sea fácil de cambiar.

Actualizaré y te aviso.

Sin embargo, hay una cosa en particular sobre la que me gustaría obtener opiniones. Son las cosas de ClassName . El makeStyles devuelve un objeto con los mismos accesorios que el que se pasó, pero donde cada elemento (JSS) ha sido reemplazado por una cadena, que es el nombre de la clase a usar (y se puede pasar a, por ejemplo, prop.className ).

Ahora, no hay forma de escribir eso en F#, así que tengo que trabajar con lo que tengo. Normalmente, todos los accesorios del objeto de estilo son IStyleAttribute list . Esto significa que puedo agregar una sobrecarga para prop.className que acepta IStyleAttribute list , lo que por supuesto es una mentira ya que en tiempo de ejecución es una cadena. Si realmente lo pasara IStyleAttribute list fallaría. Además de prop.className , esto también se aplica a todos los classes.<element>.<classesName> (usados ​​en <element>.classes [ ... ] ). También aceptan un nombre de clase (cadena).

Lo que he hecho, como puede ver, es "requerir" que todas las propiedades IStyleAttribute list del objeto de estilo se envuelvan en asClassName , que básicamente se desempaqueta a IClassName (un proxy para string , si lo desea). Y luego agregué una sobrecarga a prop.className aceptando IClassName , e hice que todos los accesorios classes aceptaran IClassName . Me gusta que esté tipeado con más fuerza, pero no me gusta que requiera tipeo adicional ( asClassName para cada regla de CSS de nivel superior). El compilador se quejará si te lo pierdes, pero no te dirá qué hacer, y sigue siendo un ruido extra.

¿Tiene alguna entrada sobre esto?

Además, me di cuenta de esto:

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

Aquí, se requieren paréntesis dobles, ya que, de lo contrario, F# lo interpreta como si intentara llamar a listItem.divider con el parámetro (inexistente) page establecido en Home (en lugar de value parámetro page = Home ). ¿Ves alguna forma de evitar eso?

Hola @cmeeren , antes que nada, me encanta esta sintaxis:

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

¡Se ve tan limpio y tan simple! Aunque si yo fuera usted, probablemente habría duplicado algunas de las funciones genéricas prop en accesorios específicos de componentes como appBar.className en lugar de (o al lado) prop.className para que todos parecen simétricos, pero lo más importante es dar la sobrecarga de IClassName al componente específico de Mui en lugar del genérico prop.className que toma una cadena porque makeStyles también es específico de Mui build y tiene sentido que solo se aplique a los componentes de Mui.

Creo que abordaste los makeStyles la mejor manera posible, al menos en este momento no puedo pensar en una mejor manera (aunque no soy un gran admirador de asClassName , tal vez Styles.createClass en su lugar? Depende de usted).

En cuanto a listItem.divider ((page = Home)) es complicado, podría agregar una función ficticia let when (x: bool) = x pero eso es solo ruido. Creo que es mejor archivarlo como un error del compilador porque no puedo pensar en la razón por la cual el compilador F # no puede resolver la sobrecarga adecuada de la función, no lo he probado, pero lo investigaré cuando el tiempo lo permita.

Por último, no soy tan receptivo como de costumbre esta semana porque ahora estoy de vacaciones, por lo que es posible que no pueda leer/verificar todo, etc.

Aunque si yo fuera usted, probablemente habría duplicado algunas de las funciones genéricas prop en accesorios específicos de componentes como appBar.className en lugar de (o al lado) prop.className para que todos parecen simétricos, pero lo más importante es dar la sobrecarga de IClassName al componente específico de Mui en lugar del genérico prop.className que toma una cadena porque makeStyles también es específico de Mui build y tiene sentido que solo se aplique a los componentes de Mui.

Compruébalo ahora :) He realizado mejoras y expansiones drásticas en los últimos días, solo las empujé (no hecho, actualmente estoy revisando todos los componentes de MUI para verificar y mejorar los accesorios generados).

Creo que abordaste los makeStyles la mejor manera posible, al menos en este momento no puedo pensar en una mejor manera (aunque no soy un gran fanático de asClassName , tal vez Styles.createClass en su lugar? Depende de usted).

Me gustaría que sea lo más breve posible, ya que se usará mucho, pero estoy abierto a otros nombres. Aunque estoy medio decidido a tener una sobrecarga IStyleAttribute list . Eliminará potencialmente bastante ruido, y dudo que sea muy peligroso incluso si técnicamente se puede usar incorrectamente.

En cuanto a listItem.divider ((page = Home)) es complicado, podría agregar una función ficticia let when (x: bool) = x pero eso es solo ruido. Creo que es mejor archivarlo como un error del compilador porque no puedo pensar en la razón por la cual el compilador F # no puede resolver la sobrecarga adecuada de la función, no lo he probado, pero lo investigaré cuando el tiempo lo permita.

Gracias, presenté un problema ahora: https://github.com/dotnet/fsharp/issues/7423

Por último, no soy tan receptivo como de costumbre esta semana porque ahora estoy de vacaciones, por lo que es posible que no pueda leer/verificar todo, etc.

Entiendo, no hay problema. Seguiré publicando problemas si me encuentro con cosas, y solo responda en su propio tiempo.

Compruébalo ahora :) He realizado mejoras y expansiones drásticas en los últimos días, solo las empujé (no hecho, actualmente estoy revisando todos los componentes de MUI para verificar y mejorar los accesorios generados).

Se ve muy bien, con los documentos generados también 😍 ¿quizás sea hora de poner su propio repositorio?

Me gustaría que sea lo más breve posible, ya que se usará mucho, pero estoy abierto a otros nombres. Aunque tengo la mitad de la mente para tener una sobrecarga de la lista IStyleAttribute. Eliminará potencialmente bastante ruido, y dudo que sea muy peligroso incluso si técnicamente se puede usar incorrectamente.

Eso también funcionaría, las bibliotecas de Fable engañan al sistema de tipos todo el tiempo;)

Gracias, presenté un problema ahora: dotnet/fsharp#7423

¡Increíble! muchas gracias

Se ve muy bien, con los documentos generados también 😍 ¿quizás sea hora de poner su propio repositorio?

Estuve pensando en eso, pero todavía hay algunos errores importantes (por ejemplo, el n. ° 27) que preferiría resolver con la comodidad de tener todo en el mismo lugar, así que creo que lo mantendré allí hasta que esté listo. para una versión preliminar de nuget (con suerte, no será demasiado larga).

@Zaid-Ajaj Estoy a punto de terminar con Feliz.MaterialUI. Se publicará en un repositorio separado pronto. Sería genial que lo revises, primero y principal para verificar algunas decisiones de diseño y garantizar la coherencia con Feliz, pero también para verificar algunas cosas de implementación (por ejemplo, ¿estoy usando cosas que harás internamente o hay cosas que no estoy usando de Feliz pero debería estar usando).

Cuando creo el nuevo repositorio, ¿está bien si creo problemas que expliquen lo que me gustaría revisar y lo etiqueto?

Ahora he subido un borrador de Feliz.MaterialUI a cmeeren/Feliz.MaterialUI . He creado varios problemas con cosas que me gustaría revisar.

¡Te estaría muy agradecido si pudieras encontrar el tiempo para echarles un vistazo!

No hay necesidad de dedicar mucho tiempo a cada tema; Realmente solo quiero una segunda opinión por las razones mencionadas en mi comentario anterior. Estoy feliz de ir a la maleza si quieres, pero incluso "esto se ve bien" sería genial.

No hay prisa, por supuesto. :)

Impresionante trabajo @cmeeren! A primera vista, las encuadernaciones se ven muy limpias, le echaré un vistazo a cada número en los próximos días, lo prometo :sonrisa:

¡Oye! ¿Alguna posibilidad de seguir examinando los problemas? De nuevo, no hay prisa, solo un recordatorio amistoso ya que no he sabido nada de ti en un par de semanas 😃

De hecho, analicé los problemas, pero como dije antes, si una API es buena o no proviene de los casos de uso, es por eso que les pedí que publicaran una versión alfa para poder probarla, pero no he tenido la tiempo para hacerlo todavía (ha estado en el fondo de mi mente esta semana: sonrisa:)

Hola @cmeeren , supongo que podemos considerar esto resuelto, ¿verdad? Cerraré el problema, vuelva a abrirlo para una mayor discusión si es necesario

¿Fue útil esta página
0 / 5 - 0 calificaciones

Temas relacionados

mastoj picture mastoj  ·  3Comentarios

alfonsogarciacaro picture alfonsogarciacaro  ·  5Comentarios

alfonsogarciacaro picture alfonsogarciacaro  ·  6Comentarios

alfonsogarciacaro picture alfonsogarciacaro  ·  6Comentarios

cmeeren picture cmeeren  ·  6Comentarios