Rendu des composants
Un <bext-include> peut pointer vers un fichier de composant — bext le compile et le SSR dans son pool d'isolation V8, avec une hydratation facultative côté client via le système d'îlot.
<bext-include src="./components/PricingTable.tsx"
render="v8"
props='{"plan":"pro","locale":"fr"}'
></bext-include>
bext compile le TSX, exécute le composant exporté dans un contexte V8 avec les accessoires et émet le code HTML SSR en ligne. Pas de processus Bun, pas de framework d'exécution, pas d'étape de construction distincte.
Attributs#
| Attribut | Valeurs | But |
|---|---|---|
render |
v8, quickjs |
Quel moteur utiliser |
props |
Chaîne JSON | Accessoires transmis au composant |
hydrate |
booléen | Sortie d'enveloppe pour l'hydratation du client |
Comment ça marche#
<bext-include src="./components/PricingTable.tsx" render="v8" props='{"plan":"pro"}'>
↓
1. Resolve file path relative to site root
↓
2. Check bundle cache
HIT → load cached compiled bundle
MISS → compile with Turbopack/SWC
· strip TypeScript types
· resolve imports
· bundle into IIFE
→ store in L1 mmap cache
↓
3. Checkout V8 isolate from pool
↓
4. Execute in isolate context with:
· component module
· props (parsed JSON)
· request context (headers, cookies, locale)
↓
5. Call renderToString() → HTML
↓
6. Return isolate to pool
↓
7. Optionally wrap in <bext-island> for hydration
↓
8. Return HTML
Le pool V8 est préchauffé au démarrage. Temps de rendu typiques :
- Composant simple (nav, bouton) : ~1-5ms
- Composant moyen (liste, formulaire) : ~5-20 ms
- Composant complexe (graphique, tableau) : ~20-100 ms
Combinez-le avec la mise en cache ttl pour amortir le coût sur de nombreuses requêtes.
Sélection du moteur#
| Moteur | Indicateur de fonctionnalité | Utiliser quand |
|---|---|---|
v8 |
v8 |
Défaut; meilleures performances sous Linux/x86 |
quickjs |
(toujours) | Petite taille binaire ; plus lent mais toujours activé |
Activez le moteur dont vous avez besoin :
# bext.config.toml
[include.v8]
enabled = true
max_execution_ms = 500
max_memory_mb = 64
pool_size = 4
Accessoires#
Transmettez les accessoires sous forme de chaîne JSON dans l'attribut props :
<bext-include src="./Hero.tsx"
render="v8"
props='{"title":"Welcome","subtitle":"Build fast","showCta":true}'
></bext-include>
Les accessoires sont analysés au format JSON et transmis au composant :
// components/Hero.tsx
interface HeroProps {
title: string;
subtitle: string;
showCta: boolean;
}
export default function Hero({ title, subtitle, showCta }: HeroProps) {
return (
<section>
<h1>{title}</h1>
<p>{subtitle}</p>
{showCta && <a href="/signup">Sign up</a>}
</section>
);
}
Les accessoires sont inclus dans la clé de cache : différents accessoires produisent différentes entrées en cache.
Hydratation#
Par défaut, les rendus V8 produisent du HTML statique — pas de JavaScript côté client. Pour les composants interactifs, ajoutez hydrate :
<bext-include src="./Counter.tsx"
render="v8"
props='{"initial":0}'
hydrate
></bext-include>
La sortie est enveloppée dans un élément <bext-island> que le système d’îlots PRISM hydrate côté client :
<div data-bext-island="Counter" data-props='{"initial":0}'>
<button>Count: 0</button>
</div>
<script type="module">
import { hydrateIsland } from '/__bext/islands/Counter.js';
hydrateIsland(document.querySelector('[data-bext-island="Counter"]'));
</script>
Le JS côté client du composant est regroupé séparément et chargé uniquement pour les cibles insulaires. Aucun coût d'hydratation à l'échelle de la page.
Fonctions d'hôte#
Les rendus V8 ont accès aux fonctions hôtes bext — les mêmes que celles disponibles en SSR pleine page :
// components/UserDashboard.tsx
import { bext } from '@bext/platform';
export default async function UserDashboard(props) {
const user = await bext.fetch('/api/user/me', {
headers: { Cookie: bext.headers.cookie }
});
const cached = await bext.cache.get(`dashboard:${user.id}`);
return <div>{/* ... */}</div>;
}
API disponibles :
bext.cache— obtenir/définir/invaliderbext.realtime— publier sur les chaînesbext.fetch— Récupérations HTTPbext.headers/bext.cookies— contexte de demandebext.logger— journalisation structurée
Mise en cache du bundle#
Les bundles compilés sont mis en cache dans le magasin mmap L1, indexés par le mtime du fichier source. La modification d'un composant déclenche la recompilation ; sinon, le bundle est chargé depuis le cache à chaque requête.
First request to /Counter.tsx
→ source mtime = 1712000000
→ compile with Turbopack
→ store bundle at .bext/cache/bundles/{hash}.js
→ execute
Subsequent requests (file unchanged)
→ mtime check passes
→ load cached bundle from mmap (microseconds)
→ execute
You edit Counter.tsx
→ mtime changes → re-compile → update cache
Dev mode: file watcher triggers immediate invalidation
Limites de carburant#
Les rendus V8 s'exécutent sous le bac à sable de bext avec des limites d'exécution basées sur le carburant — le thread de surveillance interrompt l'exécution si elle prend trop de temps :
[include.v8]
max_execution_ms = 500 # hard kill at 500ms
max_memory_mb = 64 # hard kill at 64MB
Un composant incontrôlé ne peut pas bloquer le serveur. Si l'exécution dépasse la limite, l'inclusion renvoie None et la chaîne de secours entre en jeu.
Les rendus V8 sont chers : un composant complexe peut prendre 20 à 100 ms. Associez toujours un render="v8" à un ttl pour amortir le coût. L'exécution de V8 à chaque requête sans mise en cache saturera le pool en cas de trafic réel.
Combinaison avec la mise en cache#
Les rendus V8 sont chers. Associez-le toujours avec ttl :
<bext-include src="./PricingTable.tsx"
render="v8"
props='{"plan":"pro"}'
ttl="1h"
vary="locale"
></bext-include>
- Première requête : rendu V8 (~20ms).
- Requêtes suivantes : accès au cache (~50µs).
- Changement de paramètres régionaux : nouvelle entrée de cache ; Le rendu V8 se reproduit pour ces paramètres régionaux.
Combiner avec le streaming#
Les rendus V8 peuvent être diffusés paresseusement :
<bext-include src="./HeavyChart.tsx"
render="v8"
props='{"data":"..."}'
loading="lazy"
timeout="500ms"
>
<div class="chart-skeleton"></div>
</bext-include>
Shell arrive avec le squelette. Le rendu V8 se produit en arrière-plan. Le morceau arrive lorsqu'il est prêt.
Sécurité#
Les rendus V8 s'exécutent dans un contexte isolé :
- Pas d'accès au système de fichiers hôte (au-delà de la lecture des partiels configurés).
- Aucun accès aux variables d'environnement sauf celles explicitement transmises.
- à consommation limitée — pas de boucles infinies.
- à mémoire limitée — pas d'attaques par épuisement du tas.
- connecté au réseau : l'hôte
fetchrespecte une liste d'autorisation configurée par site.
Le bac à sable V8 est le même que celui utilisé pour le système de plugins de bext, qui a été audité pour l'exécution côté serveur de code non fiable.
Le bundle compilé est mis en cache par le mtime du fichier source — la modification d'un fichier de composant déclenche la recompilation à la requête suivante. En mode développement, l'observateur de fichiers invalide immédiatement le bundle afin que vous n'ayez pas à redémarrer le serveur.
Quand utiliser les rendus V8#
Utiliser les inclusions rendues en V8 lorsque :
- Vous disposez de composants React /JSX qui nécessiteraient autrement un processus Bun SSR distinct.
- Vous souhaitez un SSR par fragment sans pipeline SSR pleine page.
- Vous avez besoin de composants interactifs avec hydratation pour des parties spécifiques d'une page.
- Vous migrez depuis un framework mais souhaitez conserver certains composants SSR.
N'utilisez pas les rendus V8 lorsque :
- Le HTML statique est suffisant (utilisez des fichiers partiels).
- Le contenu est purement basé sur les données (utilisez un point de terminaison qui renvoie du HTML).
- Vous effectuez le rendu à chaque requête sans mise en cache (la V8 coûte cher).
Voir aussi#
- Architecture des îles — le système d'hydratation V8 rend l'utilisation
- La mise en cache inclut — essentiel pour les performances de rendu V8
- React, Preact, Solide — Exécution JSX de bext
- Système de plugins — Architecture sandbox de V8