ENbext.dev
DocumentationInclusions côté serveur

Traduction automatique depuis l’anglais. Consulter l’original.

Sur cette page

Le streaming comprend

Par défaut, inclut la résolution en ligne : la réponse attend que chaque inclusion soit prête avant que le premier octet n'atteigne le client. C'est bien pour les inclusions rapides (accès au cache, résolveurs simples). C'est du gaspillage lorsqu'une inclusion lente bloque une page autrement rapide.

loading="lazy" dissocie l'inclusion de la réponse initiale. bext envoie un espace réservé dans le shell, commence à résoudre l'inclusion en parallèle et diffuse le contenu final sous forme de bloc ultérieur.

html
<bext-include src="/partials/recommendations"
  loading="lazy"
>Loading recommendations...</bext-include>
  • Réponse initiale : <div data-bext-include-id="inc_1">Loading recommendations...</div>
  • Morceau ultérieur : <script>/* swap placeholder with resolved HTML */</script>

Aucune dépendance au framework. Non React. HTML pur + un petit script en ligne.

Quand utiliser le chargement paresseux#

La paresse a raison quand :

  • L'inclusion est lente (API en amont, rendu V8, échec du cache de fragments).
  • L'inclusion est sous le pli (barre latérale de recommandations, articles associés, flux en direct).
  • L'inclusion est non critique (peut arriver une seconde plus tard sans endommager la page).

Désireux (par défaut) a raison lorsque :

  • L'inclusion est dans la fenêtre d'affichage initiale (navigation, héros, contenu principal).
  • L'inclusion modifie la mise en page lorsqu'elle arrive (ce qui entraînerait CLS).
  • L'inclusion est requise pour le référencement (crawlers qui n'exécutent pas JS).
Attention

Les robots d'exploration et les clients non JS voient le texte de l'espace réservé : ils ne reçoivent jamais le contenu diffusé. Fournissez toujours un texte de secours significatif entre les balises d'ouverture et de fermeture. bext détecte automatiquement les robots d'exploration courants et force le mode synchrone, mais les robots personnalisés peuvent ne pas être détectés.

Comment fonctionne le streaming paresseux#

Le pipeline d'inclusion s'exécute en deux phases :

code
Phase 1: Initial HTML pass
  - Eager includes resolve inline (current behavior)
  - Lazy includes emit placeholders, register with streaming pipeline

Phase 2: Streaming flush
  - Initial shell is sent to the client
  - Pending lazy includes resolve concurrently
  - As each resolves, emit a <script> chunk that swaps the placeholder
  - Final completion chunk

L'espace réservé contient la source de l'inclusion, afin que le client sache ce qui a été résolu :

html
<div data-bext-include-id="inc_1f3"
     data-bext-include-src="/partials/recommendations">
  Loading recommendations...
</div>

Lorsque l'inclusion est résolue, bext émet :

html
<script>(function(){
var el = document.querySelector('[data-bext-include-id="inc_1f3"]');
if (!el) return;
var t = document.createElement('template');
t.innerHTML = "<section>...resolved HTML...</section>";
el.replaceWith(t.content);
})();</script>

Cela utilise le même pipeline de streaming que React Suspense : les morceaux sont livrés par ordre de priorité, compressés par morceau, et le morceau d'achèvement termine la réponse.

Contenu de secours#

Le contenu entre les balises d'ouverture et de fermeture est l'espace réservé :

html
<bext-include src="/partials/comments" loading="lazy">
  <div class="skeleton-loader"></div>
</bext-include>

Si le swap JavaScript ne s'exécute jamais (JS désactivé, bot, erreur de rendu), le chargeur squelette reste visible. Fournissez toujours un contenu de secours significatif pour les inclusions paresseuses.

Résolution simultanée#

Plusieurs inclusions paresseuses sont résolues en parallèle :

html
<bext-include src="/api/feed-a" loading="lazy">Loading A...</bext-include>
<bext-include src="/api/feed-b" loading="lazy">Loading B...</bext-include>
<bext-include src="/api/feed-c" loading="lazy">Loading C...</bext-include>

Les trois se résolvent simultanément. La page n'attend pas le plus lent ; les morceaux arrivent à mesure que chacun se termine.

L'ordre n'est pas garanti — les morceaux sont vidés dans l'ordre d'achèvement. Si vous avez besoin d'un ordre déterministe, utilisez loading="eager".

Conseil

L'ordre des fragments est non garanti. — le paresseux inclut le vidage dans l'ordre d'achèvement, pas dans l'ordre des documents. Si un ordre de rendu spécifique est important (par exemple pour la stabilité de la mise en page), utilisez loading="eager" ou structurez la page de manière à ce que l'ordre ne soit pas pertinent.

Forcer la résolution synchrone#

Les robots d'exploration et les clients non JS doivent voir le contenu complet, et non les espaces réservés. bext honore l'en-tête de requête X-Bext-Include-Mode: sync :

code
X-Bext-Include-Mode: sync

Cela force chaque inclusion loading="lazy" à être résolue en ligne : la réponse est bloquée jusqu'à ce que tout soit prêt, mais les robots d'exploration voient le code HTML complet.

bext détecte automatiquement les agents utilisateurs courants du robot et injecte automatiquement cet en-tête. Vous pouvez également le définir via un en-tête personnalisé ou un paramètre de requête pour le débogage.

Streaming avec mise en cache#

Les paresseux et les personnes en cache travaillent ensemble. La première demande paie les frais de résolution ; les requêtes suivantes atteignent le cache et, si le fragment mis en cache est suffisamment petit, ignorent complètement le paresseux :

html
<bext-include src="/partials/trending"
  loading="lazy"
  ttl="5m"
></bext-include>

L'optimiseur de streaming de bext peut choisir d'intégrer le contenu dans un accès au cache (car il est déjà résolu) plutôt que de diffuser un espace réservé. Cela évite les morceaux de script inutiles lorsque les données sont déjà disponibles.

Amélioration progressive#

Les inclusions paresseuses sont adaptées aux améliorations progressives :

  • JS activé : espace réservé → contenu diffusé via un script en ligne.
  • JS désactivé : l'espace réservé reste (repli significatif).
  • Connexion lente : espace réservé visible jusqu'à ce que le réseau rattrape son retard.
  • Crawler : mode synchrone, contenu complet en HTML initial.

Aucune bibliothèque groupée côté client. Le script d'échange contient quelques octets en ligne par inclusion.

Compression par fragment#

Le pipeline de streaming de bext compresse chaque morceau indépendamment. Les petits morceaux (< 256 octets) ignorent la compression pour éviter une surcharge ; les morceaux plus gros obtiennent gzip/brotli/zstd en fonction du Accept-Encoding du client.

code
Shell: ~2KB → brotli compressed
Lazy chunk: ~5KB → brotli compressed
Completion: ~20B → uncompressed

Chaque morceau s'insère dans un segment TCP lorsque cela est possible, minimisant ainsi le TTFB pour le shell tout en gardant la taille de transfert de réponse complète efficace.

Gestion des erreurs#

Si une inclusion paresseuse ne parvient pas à être résolue :

  • Avec onerror="continue" : tombe silencieusement (l'espace réservé reste).
  • Avec l'attribut fallback : essaie la source de secours et la diffuse.
  • Avec le contenu de secours en ligne : le contenu de l'espace réservé (qui s'est déjà affiché) reste visible.
  • Sinon : une erreur de console est journalisée ; l'espace réservé reste.

Le shell a déjà été envoyé — bext ne peut pas faire échouer la réponse de manière rétroactive. Il ne peut mettre à jour que le morceau.

Interaction avec d'autres fonctionnalités#

Lazy fonctionne avec tous les autres attributs d'inclusion :

html
<bext-include
  src="/partials/personalized"
  loading="lazy"
  auth="required"
  vary="role"
  ttl="5m"
  timeout="500ms"
  fallback="/_partials/default.html"
>Loading...</bext-include>
  • La vérification d'authentification a lieu avant, l'espace réservé est émis (pas de streaming gaspillé).
  • La recherche du cache s'effectue lors de la résolution, pas lors de l'émission du shell.
  • Le délai d'attente s'applique à la résolution d'arrière-plan.
  • La chaîne de secours s'exécute si la source principale échoue.

Voir aussi#

Modifier cette page ↗Besoin d’aide ? ↗
Rechercher dans la documentation

Saisissez un sujet, une commande ou une question.