3 points par GN⁺ 2024-08-17 | 1 commentaires | Partager sur WhatsApp

Exemples de Vanilla JSX

Et si JSX renvoyait des éléments DOM ?

  • La fonction ClickMe crée un bouton et affiche le nombre de clics
  • Le texte est mis à jour à chaque clic sur le bouton
export default function ClickMe() {
  let i = 0;
  const el = Click me as HTMLButtonElement;
  el.onclick = (e) => {
    el.textContent = `Clicked ${++i} times`;
  };
  return el;
}

Réutilisabilité

  • Le composant ClickMe peut être utilisé plusieurs fois, chaque instance conservant son propre état
import ClickMe from "./sample1.js";
export default () =>

;

Création d’un arbre DOM interactif

  • Les classes TodoInput et TodoList permettent de gérer une liste de tâches
  • Il est possible d’ajouter des éléments et de les supprimer par 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}
  ;
};

Traitement de grandes quantités de données

  • La fonction FindNames traite et filtre un grand volume de données pour afficher les résultats
  • Les éléments correspondants sont mis à jour en temps réel selon la valeur saisie
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');
}

Présentation d’imlib

  • imlib est une bibliothèque développée pour immaculatalibrary.com
  • Elle a été utilisée pour construire minigamemaker.com ainsi que le site web que vous êtes en train de lire
  • Elle a été créée parce que les solutions existantes ne suffisaient pas, et c’est la méthode préférée de l’auteur pour créer des applications

Résumé de GN⁺

  • Cet article explique comment créer directement des éléments DOM et interagir avec eux à l’aide de JSX
  • Il présente une manière de traiter efficacement de grands volumes de données sans utiliser de DOM virtuel
  • La bibliothèque imlib permet de développer des applications de façon simple et intuitive
  • D’autres projets aux fonctionnalités similaires incluent React et Vue.js

1 commentaires

 
GN⁺ 2024-08-17
Avis sur Hacker News
  • Renvoyer de vrais nœuds DOM neutralise presque totalement deux grands avantages de JSX et des bibliothèques non-JSX comme Lit : une API de style mode immédiat et le modèle UI=f(state)
    Il faut renvoyer non pas le DOM réel, mais une description du DOM, afin de pouvoir réévaluer le template avec le nouvel état et mettre à jour efficacement le DOM dans lequel ce template est rendu
    Les exemples mettent à jour l’interface avec des API DOM impératives comme input.onkeydown = ... ou this.ul.append(item), alors que le principal intérêt du DOM virtuel est probablement d’éviter ce genre de code et de parcourir les éléments dans le template
    Cela dit, le problème du DOM virtuel est que le calcul des différences est lent, et on peut le résoudre avec des templates qui séparent les parties statiques des parties dynamiques, comme Lit

    • Pour avoir créé des UI web complexes, je pense que, dans une application suffisamment complexe, les avantages de performance du DOM virtuel relèvent presque du fantasme
      Au contraire, cela pousse à se dire « on peut rerender une grosse UI comme on veut, le DOM virtuel s’en chargera », ce qui se retourne contre vous : les performances réelles sont mauvaises, mais la structure de l’application est déjà figée au point d’être difficile à corriger sans réécriture
      J’ai vu exactement le même schéma dans quatre équipes et, en tant qu’architecte, j’ai recommandé une structure vanilla soucieuse des performances en m’appuyant sur les problèmes que l’on rencontrerait avec React, mais cela n’a pas été entendu, avec des réponses du type « il y a plein de façons d’éviter ça »
      Au final, dans beaucoup de cas, il semble qu’il faille apprendre à la dure
    • Cela dépend des usages. Vu autrement, JSX n’est qu’un autre DSL pour le schéma d’appels de fonction createElement, et les avantages décrits viennent du framework et du runtime
      En plus court, JSX est une syntaxe alternative d’appel de fonction qui a des cas d’usage utiles
      Dans une ancienne entreprise, nous utilisions JSX pour des factories de données de test : cela ressemblait à du XML, mais à l’étape de création des éléments, le pattern builder permettait de prendre des décisions contextuelles adaptées aux données finales
      Cela n’avait rien à voir avec React ni avec le DOM, et cela ne voulait pas dire non plus qu’il était impossible de l’exprimer de façon déclarative sans JSX
    • Ce que « l’on veut » dépend de qui est ce « on ». Personnellement, je préfère mettre à jour les nœuds DOM en place, sans aucun dispositif de réconciliation
    • Je comprends le rêve de « ne plus avoir à faire de mises à jour impératives ». J’utilise ce type de framework depuis une dizaine d’années et je les connais suffisamment bien
      Personnellement, je préfère mettre à jour le DOM de façon impérative. On peut contrôler très finement ce qui se passe et concevoir une structure très efficace
      On peut aussi rendre les fonctionnalités de l’application faciles à ajouter, modifier, supprimer ou corriger sans devoir penser selon la méthodologie très affirmée de quelqu’un d’autre
    • Je suis d’accord avec l’idée que renvoyer de vrais nœuds DOM affaiblit les avantages de JSX et des bibliothèques non-JSX comme Lit — une API de style mode immédiat et le modèle UI=f(state) —, mais cela ressemble à l’une des abstractions qui fuient les plus marquées de toute l’informatique
      Ce n’est pas un hasard si tout un écosystème s’est formé autour de React pour éviter les rendus multiples ou empêcher les rafraîchissements inutiles
  • J’ai créé ce projet parce que l’état des générateurs de sites statiques me frustrait depuis dix ans
    Comme je crée surtout des sites web statiques, je voulais un outil simple et intuitif, et JSX semblait bien convenir, mais j’en avais assez de la complexité excessive des frameworks JSX comme React
    J’ai donc créé un générateur de sites statiques qui rend JSX en chaînes de caractères, et l’étendre côté navigateur pour rendre JSX en éléments DOM était naturel
    Dans certains cas, notamment pour les layouts, cela fonctionne aussi bien avec des composants partagés. Dans l’ensemble, je suis satisfait du résultat, même si certaines parties sont assez hacky et que la prise en charge par les IDE pourrait être meilleure
    En plus, cette approche fonctionne bien pour le SEO. C’était un problème que je n’avais pas l’impression de voir correctement résolu dans les autres frameworks JSX

    • Je me demande si tu as regardé Astro. Cela ressemble à un générateur de sites statiques parfait, même s’il y avait peut-être d’autres frustrations que VanillaJSX résout
    • Bonne idée. Sur la page de démo, l’application de tâches renvoie une 404
      Dans la documentation, il vaudrait sans doute mieux écrire « static site generator » en toutes lettres plutôt que « SSG ». J’ai déjà utilisé des générateurs de sites statiques, mais je ne savais pas ce que signifiait SSG et j’ai dû demander à une IA
    • Ce serait chouette d’avoir du JSX côté serveur pour Django
    • Je me demande si tu as regardé lit-html
      Venant de Vue, j’ai été surpris : avec de simples templates vanilla JS, lit-html fait beaucoup de choses que font les templates Vue, y compris la connexion des événements
      Avec l’extension VSCode lit, on obtient aussi la coloration syntaxique et une vérification complète des types à l’intérieur des templates
      Je l’ai découvert après un tweet de Marc Grabanski disant qu’il utilisait lit-html avec vanillajs, et non Lit ; après avoir expérimenté moi-même, ça fonctionnait très bien. Cela semble assez proche de ce que tu essaies de résoudre
      Avec le package de templates lit-html, on peut faire la plupart de ce qui est décrit dans le chapitre sur les templates : https://lit.dev/docs/templates/overview/
      C’est possible même sans l’abstraction de composants de lit-element : https://lit.dev/docs/libraries/standalone-templates/#renderi...
  • Ce genre de « et si… » est amusant parce que l’origine de JSX remonte jusqu’à XHP de Facebook, et que XHP s’inspirait explicitement d’un ancien standard JS, E4X, qui ressemblait à la bibliothèque décrite ici et fonctionnait de façon similaire
    [1] https://engineering.fb.com/2010/02/09/developer-tools/xhp-a-...
    [2] https://en.m.wikipedia.org/wiki/ECMAScript_for_XML

    • E4X avait le fâcheux défaut de renvoyer de véritables instances DOM, qu’il fallait donc mettre à jour de manière impérative
      C’est aussi pour cela que JSX a dépassé E4X, et qu’aucune proposition sérieuse de templates HTML dans JS n’a ensuite émergé
      Cela dit, on pourrait sans doute remettre l’idée générale au goût du jour : https://github.com/WICG/webcomponents/issues/1069
    • Fait amusant : si JavaScript a reçu for(of) au lieu de for each, c’est à cause d’E4X
      La raison pour laquelle on n’a pas eu for (:) est encore plus absurde : cela aurait pu entrer en conflit avec des annotations :type que certains membres du TC39 croyaient voir arriver un jour comme par magie dans le langage
  • J’ai déjà créé une bibliothèque d’UI autour de l’idée que des expressions de template JSX produisent de vrais nœuds DOM
    Elle permettait aussi de lier des objets modèle à des attributs, supprimant ainsi une partie du boilerplate des gestionnaires d’événements impératifs. Évidemment, c’est ce qu’on pourrait penser, mais je trouve que c’est une bonne idée
    https://github.com/tomtheisen/mutraction
    On peut écrire du code du genre créer un modèle avec track({ clicks: 0 }), incrémenter model.clicks au clic, et voir cette valeur reflétée directement dans le DOM

  • VanJS mérite aussi d’être mentionné ici : https://vanjs.org/
    D’autres bibliothèques JSX comme Solid.JS renvoient aussi des nœuds DOM, et j’aime voir cette idée gagner du terrain
    Je pense que plus on se rapproche de la plateforme qu’on utilise, mieux c’est. Les couches d’abstraction peuvent parfois être utiles, mais en pratique je n’ai pas encore trouvé de raison de cacher la plateforme
    D’énormes projets comme ceux de Facebook y trouvent peut-être un intérêt, mais je n’ai jamais travaillé à cette échelle

    • Dans la comparaison des tailles de bundle, SolidJS n’est-il pas un peu hors sujet ?
  • Les exemples sont chouettes, mais il me semble important qu’ils ne montrent pas de props de composants susceptibles de changer avec le temps
    Cette capacité elle-même ne semble pas modélisée. Si on n’en a pas besoin, c’est une approche astucieuse, mais je vois mal comment elle s’étendrait à des applications plus complexes

    • La technique utilisée ici, et dans tout le code côté navigateur, est exactement la même que celle utilisée en interne par VS Code, et elle passe plutôt bien à l’échelle
      La seule différence est qu’elle exprime plus succinctement les 10 lignes de code qui créeraient et configureraient normalement des éléments DOM
      En réalité, ce qui est vraiment intéressant dans ce framework, c’est presque tout le reste. Côté générateur de site statique, il y a le fait que JSX renvoie des chaînes, l’import d’un répertoire source brut pour manipuler des string|Buffer au moment du SSG, un système de modules maison très efficace et rapide construit sur chokidar et swc, etc.
      La partie qui utilise JSX comme DOM est simplement la plus visible, mais en pratique c’est plutôt un tour de passe-passe
      Par exemple, le source de vanillajsx.com est très concis, clair et court. J’ai tout écrit aujourd’hui, sans dépendance en dehors de imlib, et la démo JSX-as-DOM est la partie la moins innovante : https://github.com/sdegutis/vanillajsx.com/tree/main/site
    • Pour faire passer cette approche à une vraie échelle et être sûr que tout fonctionne ensemble comme prévu, il faut une vraie couche de réactivité
      Si vous aimez cette façon où JSX renvoie des éléments DOM concrets, Solid fonctionne exactement ainsi, avec une vraie couche de réactivité en plus
    • J’ai peut-être raté quelque chose, mais je ne vois pas comment cette approche empêche l’usage de setTimeout ou de setInterval
      Cela dit, je suis d’accord pour dire que ces projets fonctionnent souvent bien sur de petits cas d’usage, mais s’effondrent vite dans des scénarios « réels »
    • Je ne cherche pas à défendre cette bibliothèque et je n’ai pas prévu de l’utiliser, mais pour que les changements de props déclenchent un rerender comme dans React/Vue, il me semble qu’on pourrait utiliser un JS Proxy ici
      L’implémentation ne devrait pas être si difficile
  • C’est très proche de Vanilla TSX : https://github.com/wisercoder/uibuilder
    Un exemple d’application écrite en Vanilla TSX se trouve ici : https://github.com/wisercoder/eureka/tree/master/webapp/Clie...

  • Ça me rappelle ActionScript 3, qui avait XML au cœur du langage
    C’était un langage agréable à utiliser, mais il a notoirement échoué à devenir ES4. Au final, il aura fallu plus de dix ans pour arriver à quelque chose de similaire avec TypeScript et JSX

    • https://en.wikipedia.org/wiki/ECMAScript_for_XML — Firefox l’avait aussi, mais le grand public n’en voulait pas et il a fini par être supprimé
      Il a été désactivé pour les pages web dans Firefox 17, six mois avant la première version de React
    • Je ne me souviens pas qu’on pouvait créer du XML en ligne de cette manière. Peut-être que c’était possible si c’était une fonctionnalité côté serveur de Flex
  • Je me demande si d’autres personnes ont le dernier exemple qui ne fonctionne pas dans Firefox. Ça marchait dans Edge, mais ça a échoué dans Firefox
    Uncaught (in promise) TypeError: Map.groupBy(...).entries().map is not a function

  • Je ne comprends pas bien en quoi JSX est plus séduisant que d’écrire quelque chose comme ci-dessous
    h("div", {}, [ h("p", {}, "this is easy"), ...list.map((l) => h("li", {}, l)) ])
    En faisant ainsi, on obtient automatiquement les boucles, l’interpolation de variables, etc., sans avoir à créer un compilateur ni une nouvelle syntaxe. J’aimerais bien que quelqu’un me l’explique

    • Je pense que beaucoup de gens trouvent la version JSX bien plus lisible et familière. Bien sûr, c’est une question de goût
      Pour être juste, avec JSX aussi on utilise tels quels les boucles et l’interpolation ordinaires. Les {} acceptent des expressions JS vanilla
      Il faut simplement une étape de compilation pour déplier les balises d’éléments en appels à createElement
    • Personnellement, quand je vois le code donné en exemple, j’ai du mal à comprendre les personnes qui pensent que c’est aussi bien que JSX
      Chacun traite l’information différemment, mais cet exemple contient tellement de ponctuation qu’il est très difficile à saisir rapidement
      À moins de décomposer soi-même la syntaxe dans sa tête, on ne voit pas la structure du DOM généré ; comprendre la structure du DOM d’un coup d’œil est bien plus important que la question de savoir s’il faut un compilateur ou non
      Pour info, je suis moi-même concerné par la neurodiversité, et j’espère que cette explication sera utile
    • Si l’on descend encore dans le terrier de ce raisonnement, on finit par arriver à Common Lisp
    • J’ai déjà eu la même réflexion. Un avantage, à mon avis, est que ça ressemble à du HTML
      C’est proche de ce qu’on voit dans les outils de développement du navigateur, donc c’est plus facile à comparer et à déboguer
    • Pour un fichier unique ou une appli simple, ce style peut très bien convenir
      Mais dans une appli complexe, quand on se retrouve avec des centaines de composants qui commencent à s’importer les uns les autres, l’écriture avec des balises JSX donne l’impression d’une syntaxe simple et facile à comprendre
      Les réactions hostiles à React viennent aussi en grande partie du fait que c’est excessif pour des choses simples, et ça l’est effectivement
      Mais comme les applications, leur complexité et leurs exigences varient selon les personnes, on ne peut pas simplifier en disant qu’un côté est plus séduisant que l’autre. Ce sont des outils différents pour des problèmes différents