Templates html``
Fluixi permet d'écrire du balisage de deux façons : en JSX, ou avec un template
balisé html . Les deux compilent vers les *mêmes* appels DOM à granularité fine : il n'y a aucune différence à l'exécution. html est une surface d'écriture sans JSX,
pour quand vous ne voulez pas configurer JSX dans votre tsconfig.
import { html } from '@fluixi/core';
function Hello(props: { name: string }) {
return html`<h1>Bonjour ${props.name}</h1>`;
}
Importez html (et svg) depuis @fluixi/core, ou depuis @fluixi/start dans une
application méta-framework.
Comment ça compile
C'est la clé de tout ce qui suit. Le template n'est pas analysé à l'exécution. À la compilation, le compilateur le sépare en balisage statique et en trous, émet la partie statique une seule fois, et relie chaque trou à un appel DOM ciblé :
html`<p class="row">Compteur : ${count()}</p>`;
devient, en substance, un <p class="row"> construit une fois et un unique nœud texte
tenu à jour par un effet. Rien n'est re-rendu, rien n'est comparé.
Deux conséquences à connaître :
- La valeur d'un trou n'est jamais analysée comme du HTML. Elle est affectée comme
texte, comme attribut ou comme propriété. Interpoler une chaîne contenant
<script>insère ce texte, cela ne crée pas d'élément. La seule exception délibérée esthtml=, documentée comme une échappatoire pour cette raison même. - La forme du template est figée à la compilation. Un trou peut fournir une valeur, un enfant, un composant ou un ensemble de props — mais pas un nom de balise ni un nom d'attribut, car ceux-là déterminent la forme. Voir les composants pour le cas des balises dynamiques.
Interpolation
Tout ${…} est un trou réactif. Lire un signal à l'intérieur maintient cet endroit — et
uniquement celui-là — à jour :
const [count, setCount] = createSignal(0);
html`<p>Compteur : ${count()}</p>`;
Où placer un trou
html`<p>${text()}</p>`; // enfant — texte, nœud, tableau, autre template
html`<div title=${label()}></div>`; // valeur d'attribut entière
html`<div class="card ${theme()}"></div>`; // partie d'une valeur entre guillemets
html`<div ...${attrs}></div>`; // un objet de props entier
html`<${Card} />`; // un composant en position de balise
Un trou qui remplit toute la valeur d'un attribut la transmet telle quelle : elle peut
donc être de n'importe quel type — un nombre, un booléen, un objet pour class/style.
Les guillemets n'y changent rien : value=${n} et value="${n}" compilent à l'identique.
Mêler du texte statique et un trou relève de l'interpolation de chaîne, et le résultat est toujours une chaîne :
html`<input value=${0} />`; // le nombre 0
html`<div class="card ${theme()}"></div>`; // la chaîne « card dark »
Lire un signal, ne pas le passer
Appelez l'accesseur dans le trou. Le trou est lui-même la frontière réactive : le compilateur l'enveloppe dans un effet pour vous.
html`<p>${count()}</p>`; // ✓ suivi — ne réexécute que ce nœud texte
html`<p>${count}</p>`; // affiche la fonction, pas sa valeur
Composants
Deux formes, dont la différence ne tient qu'à la façon dont TypeScript voit le nom :
// Balise dynamique — `${Card}` est une vraie expression, TypeScript la compte comme utilisée.
html`<${Card} title=${t()} />`;
// Balise statique — nom nu, comme en JSX.
html`<Card title=${t()} />`;
La forme statique a besoin de quelque chose pour signaler à TypeScript que le nom est
utilisé, puisqu'il n'est que du texte dans une chaîne : soit
@fluixi/ts-plugin, soit une
règle resolve qui fournit le composant sans import. Dans
un projet TypeScript sans l'un ni l'autre, préférez <${Card}/>.
Les enfants et les balises fermantes fonctionnent comme attendu. Une balise dynamique se ferme avec la même expression :
html`<${Card}>
<p>Contenu</p>
</${Card}>`;
html`<Card>
<p>Contenu</p>
</Card>`;
L'auto-fermeture fonctionne pour les deux formes et — contrairement à HTML — pour n'importe quel composant.
Composants auto-importés
Les composants de contrôle de flux et du routeur se résolvent sans import : écrivez
<Show>, <For>, <Router>, <Outlet> et le compilateur ajoute l'import.
html`
<${Show} when=${user()} fallback=${html`<a href="/login">Se connecter</a>`}>
<p>Bienvenue, ${user()!.name}</p>
</${Show}>
`;
Vos propres composants et ceux d'une bibliothèque peuvent les rejoindre — voir résolution des composants.
Props
Chaque prop est un trou, et chacune est réactive indépendamment :
html`<${Row} label="Statique" count=${n()} onSelect=${pick} ...${rest} />`;
Les enfants arrivent dans props.children. Un enfant fonction est transmis tel quel :
c'est ce sur quoi reposent <For> et each.
Templates imbriqués
Un trou peut contenir un autre template — pratique pour fallback, les éléments de liste
ou les branches conditionnelles :
html`<ul>${items().map((i) => html`<li>${i.label}</li>`)}</ul>`;
Un template imbriqué compile comme les autres : construit une fois, réutilisé à chaque
appel. Pour une liste qui change, préférez each ou <For> —
.map() reconstruit toutes les lignes à chaque changement, là où <For> réutilise celles
dont la donnée n'a pas bougé.
SVG
Utilisez la balise svg pour placer un sous-arbre dans l'espace de noms SVG. Les éléments
créés là en ont besoin, et la balise html ne l'applique pas :
import { svg } from '@fluixi/core';
svg`<circle cx="50" cy="50" r=${r()} />`;
Une racine <svg> écrite dans html fonctionne aussi — le compilateur reconnaît la balise et crée le sous-arbre dans le bon espace de noms. Réservez svg aux templates
qui commencent en dessous de la racine, avec un <circle> ou un <path> sans ancêtre
<svg> dans le même template pour le signaler.
Espaces et texte
Les espaces sont conservés tels quels, comme en HTML — repliés par CSS au rendu, pas par le
compilateur. <pre>, <textarea>, <script> et <style> gardent leur contenu verbatim.
Prise en charge de l'éditeur
@fluixi/ts-plugin (inclus dans toute application créée par le scaffolding, et livré dans
l'extension VS Code) donne à html `` une véritable IntelliSense : survol, complétion
des attributs d'élément et des props de composant, aller à la définition sur les balises,
et vérification de types du JavaScript dans les trous — la même expérience qu'en JSX.
Référencez-le dans tsconfig.json :
{ "compilerOptions": { "plugins": [{ "name": "@fluixi/ts-plugin" }] } }
Quand choisir html``
- Vous ne voulez pas de
jsx/jsxImportSourcedans votre tsconfig (une bibliothèque, un script, du TypeScript simple). - Vous préférez le balisage en template littéral.
- Vous voulez les directives réservées à
html`` :if/else,each,bind:,class:name,style:prop. - Vous voulez mélanger les deux — un bloc
html`` dans un composant JSX, ou l'inverse : les deux compilent.
Ensuite : la référence complète des directives (événements,
liaisons, if/each, …).