3 puntos por GN⁺ 2024-08-17 | 1 comentarios | Compartir por WhatsApp

Ejemplos de JSX vainilla

¿Y si JSX devolviera elementos del DOM?

  • La función ClickMe crea un botón y muestra la cantidad de clics
  • El texto se actualiza cada vez que se hace clic en el botón
export default function ClickMe() {
  let i = 0;
  const el = Click me as HTMLButtonElement;
  el.onclick = (e) => {
    el.textContent = `Clicked ${++i} times`;
  };
  return el;
}

Reutilización

  • El componente ClickMe puede usarse varias veces y mantener un estado distinto en cada instancia
import ClickMe from "./sample1.js";
export default () =>

;

Creación de un árbol DOM interactivo

  • Se puede administrar una lista de tareas usando las clases TodoInput y TodoList
  • Se pueden agregar elementos y eliminarlos con un clic
function TodoInput(attrs: { add: (v: string) => void }) {
  const input =  as HTMLInputElement;
  input.placeholder = 'Add todo item...';
  input.onkeydown = (e) => {
    if (e.key === 'Enter') {
      attrs.add(input.value);
      input.value = '';
    }
  };
  return input;
}

class TodoList {
  ul =  as HTMLUListElement;
  add(v: string) {
    const item =
- {v}
 as HTMLLIElement;
    item.onclick = () => item.remove();
    this.ul.append(item);
  }
}

export default () => {
  const list = new TodoList();
  list.add('foo');
  list.add('bar');
  return
     list.add(v)} />
    {list.ul}
  ;
};

Procesamiento de grandes volúmenes de datos

  • La función FindNames procesa y filtra grandes volúmenes de datos para mostrar resultados
  • Actualiza en tiempo real los elementos coincidentes según el valor de entrada
import { data } from "../fetch-dataset.js";
export default function FindNames() {
  const status =  as HTMLParagraphElement;
  const results =  as HTMLUListElement;
  const input =  as HTMLInputElement;
  updateMatches();

  function updateMatches() {
    const matched = (data.entries().filter(([k]) => k.match(input.value)).toArray());
    const matches = (Iterator.from(matched).map(match => ).take(30));
    results.replaceChildren(...matches);
    status.textContent = `${matched.length} / ${data.size}`;
  }

  return
    {input}
    {status}
    {results}

;
}

function Item(attrs: { match: [string, number], regex: string }) {
  const [name, count] = attrs.match;
  const total = ({count});
  return
-
     {total}

;
}

function highlight(str: string, regex: string) {
  if (!regex) return str;
  const r = new RegExp(`(${regex})`, 'gi');
  return str.replace(r, '$1');
}

Introducción a imlib

  • imlib es una librería desarrollada para immaculatalibrary.com
  • Se usa para construir minigamemaker.com y el sitio web que estás leyendo ahora
  • Fue desarrollada porque el estado existente no era suficiente, y es la forma preferida de crear apps

Resumen de GN⁺

  • Este artículo explica cómo crear e interactuar directamente con elementos del DOM usando JSX
  • Presenta una forma de procesar grandes volúmenes de datos de manera eficiente sin usar un DOM virtual
  • La librería imlib permite desarrollar apps de una manera simple e intuitiva
  • Otros proyectos con funciones similares incluyen React, Vue.js, etc.

1 comentarios

 
GN⁺ 2024-08-17
Opiniones en Hacker News
  • Si se devuelven nodos DOM reales, prácticamente se anulan grandes ventajas de JSX y de bibliotecas no-JSX como Lit: una API de estilo modo inmediato y el modelo UI=f(state)
    Hay que devolver una descripción del DOM, no el DOM real, para poder reevaluar la plantilla con el nuevo estado y actualizar eficientemente el DOM en el que se renderiza esa plantilla
    Los ejemplos actualizan con APIs DOM imperativas como input.onkeydown = ... o this.ul.append(item), y probablemente una de las principales ventajas del DOM virtual sea evitar ese tipo de código y recorrer los elementos desde la plantilla
    Dicho eso, el problema del DOM virtual es que el cálculo de diferencias es lento, y se puede resolver con plantillas que separen las partes estáticas y dinámicas, como Lit

    • Por mi experiencia creando UI web complejas, en apps suficientemente complejas la ventaja de rendimiento del DOM virtual me parece casi una ilusión
      Más bien produce el efecto contrario: te hace pensar “puedo volver a renderizar una UI grande a voluntad y el DOM virtual se encargará”, mientras el rendimiento real es malo y la estructura de la app ya quedó tan fija que es difícil arreglarla sin reescribirla
      Vi el mismo patrón en cuatro equipos y, como arquitecto, recomendé una estructura vanilla consciente del rendimiento basándome en los problemas que tendrían con React, pero no fue aceptado porque “hay muchas formas de evitarlos”
      Al final, parece que en muchos casos hay que aprender sufriéndolo en carne propia
    • Esto depende del usuario. Visto desde otra perspectiva, JSX no es más que otro DSL para el patrón de llamadas a la función createElement, y las ventajas descritas vienen del framework y del runtime
      Dicho más brevemente: JSX es una sintaxis alternativa de llamada a funciones que tiene usos útiles
      En una empresa anterior usamos JSX para fábricas de datos de prueba; parecía XML, pero en la etapa de creación de elementos podíamos tomar decisiones contextuales ajustadas a los datos finales mediante el patrón builder
      No tenía relación con React ni con el DOM, y tampoco significaba que no pudiera expresarse de forma declarativa sin JSX
    • Lo que “uno quiere” depende de quién sea ese uno. Personalmente prefiero actualizar los nodos DOM en su lugar, sin ningún mecanismo de reconciliación (reconciliation)
    • Entiendo el sueño de “ya no tener que hacer actualizaciones imperativas”. He usado frameworks así durante unos 10 años y estoy bastante familiarizado con ellos
      Personalmente prefiero actualizar el DOM de forma imperativa. Puedo controlar con muchísimo detalle qué ocurre y diseñarlo con una estructura muy eficiente
      Puedo hacer que sea fácil agregar, cambiar, eliminar y corregir funciones de la app sin tener que pensar según la metodología rígida de alguien más
    • Estoy de acuerdo en que devolver nodos DOM reales debilita las ventajas de JSX y de bibliotecas no-JSX como Lit —una API de estilo modo inmediato y el modelo UI=f(state)—, pero esto se siente como una de las abstracciones con fugas más notables de toda la computación
      Hay una razón por la que alrededor de React surgió todo un ecosistema sobre cómo evitar renderizados múltiples o refrescos innecesarios
  • Creé este proyecto porque durante los últimos 10 años me frustró el estado de los generadores de sitios estáticos
    Como suelo crear principalmente sitios web estáticos, quería una herramienta simple e intuitiva, y JSX parecía encajar bien, pero me cansó la complejidad excesiva de los frameworks JSX como React
    Así que hice un generador de sitios estáticos que renderiza JSX como strings, y extenderlo al lado del navegador para renderizar JSX como elementos DOM fue algo natural
    En algunos casos, especialmente en layouts, también encaja bien con componentes compartidos. En general estoy satisfecho con el resultado, aunque algunas partes son bastante hacky y el soporte de IDE podría mejorar
    Además, este enfoque también encaja bien con SEO. Era un problema que no sentía bien resuelto en otros frameworks JSX

    • Me pregunto si viste Astro. Se siente como un generador de sitios estáticos perfecto, aunque puede que hubiera otras molestias que VanillaJSX resuelve
    • Buena idea. En la página de demo, la app de tareas da 404
      En la documentación quizá convendría escribir “static site generator” completo en vez de “SSG”. He usado generadores de sitios estáticos y aun así no sabía qué era SSG; tuve que preguntarle a una IA
    • Sería genial tener JSX para el lado del servidor en Django
    • Me pregunto si revisaste lit-html
      Venía de usar Vue y me sorprendió: lit-html hace muchas de las cosas que hacen las plantillas de Vue, incluida la conexión de eventos, usando solo plantillas simples de vanilla JS
      Con la extensión de Lit para VSCode puedes tener resaltado de sintaxis y verificación de tipos completa incluso dentro de las plantillas
      Lo conocí después de que Marc Grabanski tuiteara que usaba lit-html con vanillajs, no Lit, y al probarlo por mi cuenta funcionó muy bien. Parece bastante parecido a lo que estás intentando resolver ahora
      Con el paquete de plantillas lit-html puedes hacer la mayor parte de lo que se describe en el capítulo de plantillas: https://lit.dev/docs/templates/overview/
      También se puede sin la abstracción de componentes de lit-element: https://lit.dev/docs/libraries/standalone-templates/#renderi...
  • Lo interesante de estos “qué pasaría si” es que los orígenes de JSX se pueden rastrear hasta XHP de Facebook, y XHP se inspiró explícitamente en el estándar temprano de JS E4X, que se veía y funcionaba de forma parecida a la biblioteca descrita aquí
    [1] https://engineering.fb.com/2010/02/09/developer-tools/xhp-a-...
    [2] https://en.m.wikipedia.org/wiki/ECMAScript_for_XML

    • E4X tenía la desafortunada desventaja de devolver instancias reales del DOM, así que había que actualizarlas de forma imperativa
      Por eso JSX superó a E4X, y desde entonces no hubo propuestas serias para plantillas HTML dentro de JS
      Aun así, parece que la idea general podría revivirse con un enfoque moderno: https://github.com/WICG/webcomponents/issues/1069
    • Como dato curioso, E4X es la razón por la que JavaScript incorporó for(of) en vez de for each
      La razón por la que no obtuvimos for (:) es todavía más absurda: podía chocar con anotaciones :type que algunos miembros de TC39 creían que algún día entrarían mágicamente al lenguaje
  • Alguna vez hice una biblioteca de UI basada en la idea de que las expresiones de plantilla JSX crearan nodos DOM reales
    También eliminaba parte del boilerplate de manejadores de eventos imperativos vinculando objetos de modelo a propiedades. Como seguramente imaginarán, me parece una buena idea
    https://github.com/tomtheisen/mutraction
    Permitía escribir código del estilo de crear un modelo con track({ clicks: 0 }), incrementar model.clicks al hacer clic y reflejar ese valor directamente en el DOM

  • También vale la pena mencionar VanJS aquí: https://vanjs.org/
    Otras bibliotecas JSX como Solid.JS también devuelven nodos DOM, y me gusta que esta idea esté ganando fuerza
    Creo que mientras más cerca estemos de la plataforma que usamos, mejor. Las capas de abstracción a veces pueden ser útiles, pero en la práctica todavía no encontré la necesidad de ocultar la plataforma
    Tal vez proyectos enormes como Facebook sí se beneficien, pero no he trabajado a esa escala

    • En la comparación de tamaño de bundle, ¿SolidJS no es medio irrelevante?
  • Los ejemplos están geniales, pero parece importante que no muestren props de componentes que puedan cambiar con el tiempo
    Esa capacidad en sí misma no parece estar modelada. Si no hace falta, es una forma ingeniosa, pero no veo bien cómo escalaría a apps más complejas

    • La técnica usada aquí y en todo el código del lado del navegador es exactamente la misma que se usa dentro de VS Code, y escala bastante bien
      La diferencia es solo que permite escribir de forma más concisa unas 10 líneas de código que crean y configuran elementos DOM de la manera habitual
      De hecho, lo realmente interesante de este framework es casi todo lo demás: devolver JSX como strings del lado del generador de sitios estáticos, tomar directorios de código fuente sin procesar y manipular string|Buffer en el momento de SSG, y un sistema de módulos propio, muy eficiente y rápido, construido sobre chokidar y swc
      La parte de usar JSX como DOM es la más llamativa visualmente, pero en realidad se parece más a un truco de fiesta
      Por ejemplo, el código fuente de vanillajsx.com es muy conciso, claro y breve. Lo escribí todo en un solo día sin dependencias salvo imlib, y la demo de JSX-como-DOM es la parte menos innovadora: https://github.com/sdegutis/vanillajsx.com/tree/main/site
    • Para llevar este enfoque a una escala real y tener confianza en que todo funcione junto como se espera, hace falta una capa de reactividad adecuada
      Si te gusta esta forma en que JSX devuelve elementos DOM concretos, Solid funciona exactamente así, y además le suma una capa de reactividad bien hecha
    • Quizás se me escapa algo, pero no veo cómo este enfoque evita el uso de setTimeout o setInterval
      Dicho eso, coincido en que estos proyectos suelen funcionar bien para casos de uso pequeños, pero se rompen rápido en escenarios del “mundo real”
    • No estoy defendiendo esta biblioteca ni planeo usarla, pero si se quiere que los cambios de props disparen rerenderizados como en React/Vue, acá parece que bastaría con usar JS Proxy
      No creo que la implementación sea tan difícil
  • Esto es muy parecido a Vanilla TSX: https://github.com/wisercoder/uibuilder
    Aquí hay un ejemplo de app escrita con Vanilla TSX: https://github.com/wisercoder/eureka/tree/master/webapp/Clie...

  • Me recuerda a ActionScript 3, que tenía XML en el núcleo del lenguaje
    Era un lenguaje divertido de usar, pero fracasó de forma célebre en convertirse en ES4. Al final nos tomó más de 10 años llegar a un punto similar con TypeScript y JSX

    • https://en.wikipedia.org/wiki/ECMAScript_for_XML — también estuvo en Firefox, pero el público no lo quería y finalmente se eliminó
      Se desactivó para páginas web en Firefox 17, seis meses antes del primer lanzamiento de React
    • No recuerdo que se pudiera generar XML inline de esta forma. Quizás habría sido posible si era una función del lado del servidor de Flex
  • Me pregunto si hay alguien más a quien el último ejemplo no le funcione en Firefox. En Edge sí funcionó, pero en Firefox falló
    Uncaught (in promise) TypeError: Map.groupBy(...).entries().map is not a function

  • No termino de entender por qué JSX resulta atractivo frente a escribir algo como esto:
    h("div", {}, [ h("p", {}, "this is easy"), ...list.map((l) => h("li", {}, l)) ])
    Así se obtienen automáticamente loops, interpolación de variables, etc., sin tener que crear un compilador ni una sintaxis nueva. Me gustaría que alguien me lo explicara

    • Creo que mucha gente encuentra la versión con JSX mucho más legible y familiar. Claro, es cuestión de gustos
      Para ser justos, en JSX también se usan loops e interpolación normales tal cual. {} acepta expresiones de JS vanilla
      Solo que se necesita una etapa de compilación que descomponga las etiquetas de elementos en llamadas a createElement
    • Personalmente, cuando veo el código que compartiste, no entiendo muy bien a quienes piensan que eso es tan bueno como JSX
      Cada persona procesa la información de forma distinta, pero ese ejemplo tiene tanta puntuación que es muy difícil captarlo rápido
      Si no descompongo mentalmente la sintaxis, no veo la estructura del DOM que se genera, y entender la estructura del DOM de un vistazo es mucho más importante que si hace falta o no un compilador
      Como referencia, soy una persona neurodivergente, y espero que esta explicación ayude
    • Si sigues bajando por la madriguera de esa lógica, al final llegas a Common Lisp
    • He pensado lo mismo alguna vez. Creo que una ventaja es que se parece a HTML
      Como es similar a lo que ves en las herramientas de desarrollo del navegador, es más fácil compararlo y depurarlo
    • Para un archivo único o una app sencilla, ese estilo también está bien
      Pero en apps complejas, cuando tienes cientos de componentes y empiezan a importarse unos a otros, escribirlos como etiquetas JSX se siente como una sintaxis simple y fácil de entender
      Gran parte de la reacción contra React también viene de que es excesivo para cosas simples, y de hecho lo es
      Pero cada persona crea apps con distinta complejidad y requisitos, así que no se puede simplificar diciendo que un lado es más atractivo que el otro. Son herramientas distintas para problemas distintos