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
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 = ...outhis.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 templateCela 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
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
createElement, et les avantages décrits viennent du framework et du runtimeEn 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
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
UI=f(state)—, mais cela ressemble à l’une des abstractions qui fuient les plus marquées de toute l’informatiqueCe 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
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
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
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
for(of)au lieu defor each, c’est à cause d’E4XLa raison pour laquelle on n’a pas eu
for (:)est encore plus absurde : cela aurait pu entrer en conflit avec des annotations:typeque certains membres du TC39 croyaient voir arriver un jour comme par magie dans le langageJ’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émentermodel.clicksau clic, et voir cette valeur reflétée directement dans le DOMVanJS 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
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 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|Bufferau moment du SSG, un système de modules maison très efficace et rapide construit surchokidaretswc, 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/siteSi 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
setTimeoutou desetIntervalCela 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 »
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
Il a été désactivé pour les pages web dans Firefox 17, six mois avant la première version de React
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 functionJe 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
Pour être juste, avec JSX aussi on utilise tels quels les boucles et l’interpolation ordinaires. Les
{}acceptent des expressions JS vanillaIl faut simplement une étape de compilation pour déplier les balises d’éléments en appels à
createElementChacun 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
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
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