Le streaming HTML Out-of-Order (OOO) (également appelé "mises à jour partielles déclaratives") est une alternative au traitement linéaire traditionnel "de haut en bas" du code HTML. Il permet aux développeurs de définir des espaces réservés plus tôt dans un document et de les "corriger" avec du contenu qui arrive plus tard dans le même flux réseau, ou via de nouvelles API de streaming JavaScript impératives.
Présentation rapide
Cette fonctionnalité repose sur deux mécanismes principaux :
- Application de correctifs déclarative (basée sur HTML) : utilise de nouvelles instructions de traitement (
<?marker>,<?start>,<?end>) et<template for>pour échanger du contenu dans le DOM à mesure qu'il est diffusé depuis le serveur. - Définition et streaming impératifs (basés sur JS) : suite de nouvelles méthodes (par exemple,
streamHTML(),setHTMLUnsafe()) qui permettent de définir du code HTML ou de rediriger des flux lisibles directement vers des éléments DOM avec des performances natives et une exécution de script facultative.
Bonnes pratiques
- Priorisez le contenu au-dessus de la ligne de flottaison : diffusez d'abord l'UI critique (en-tête, image principale, navigation principale, contenu principal). Utilisez des espaces réservés pour les composants "coûteux" ou de priorité inférieure, comme les commentaires, les annonces ou les contenus associés.
- Implémenter des états de chargement natifs : utilisez les repères
<?start>et<?end>pour encapsuler le contenu de remplacement. Le navigateur remplace automatiquement tout ce qui se trouve entre ces repères lorsque le<template for>correspondant arrive. - Réduisez le Cumulative Layout Shift (CLS) : fournissez toujours des dimensions stables pour les espaces réservés. Si la taille finale du contenu est inconnue, utilisez des écrans squelettes ou la propriété CSS
aspect-ratiopour réserver de l'espace et éviter les décalages brusques. - Accessibilité et gestion du focus : n'oubliez pas que les mises à jour désordonnées peuvent perturber les utilisateurs de lecteurs d'écran. Utilisez des régions
aria-livepour les mises à jour critiques et assurez-vous que le curseur n'est pas perdu si un espace réservé contenant l'élément actif est remplacé.
Détails de mise en œuvre
1. Repères HTML déclaratifs
Le navigateur recherche des instructions de traitement spécifiques pour identifier l'emplacement où le contenu doit être corrigé.
<!-- A simple insertion point -->
<div id="sidebar">
<?marker name="dynamic-comments">
</div>
<!-- A replacement range with loading state -->
<main>
<?start name="main-content">
<div class="skeleton">Loading content...</div>
<?end>
</main>
... (other content) ...
<!-- Later in the same stream, provide the content -->
<template for="dynamic-comments">
<div class="comments">...</div>
</template>
<template for="main-content">
<article>
<h1>The Real Content</h1>
<p>This replaces the skeleton div automatically.</p>
</article>
</template>
Une fois qu'un <template> remplace un point d'insertion, ce point d'insertion n'existe plus et ne peut pas être réutilisé. Toutefois, un <template> peut contenir des points d'insertion qui pourront être utilisés plus tard dans le flux.
<?start name="replaced-content">
<div class="skeleton">
<?end>
<!-- Later in the same stream, provide initial content -->
<template for="replaced-content">
<?start name="replaced-content">
<div>Quickly computed result</div>
<?end>
</template>
<!-- Later in the same stream, provide full content -->
<template for="replaced-content">
<?start name="replaced-content">
<div>Full result</div>
<?end>
</template>
2. Streaming JS impératif
Le navigateur fournit une suite de 24 méthodes (6 actions, chacune avec 4 variantes) pour mettre à jour le DOM de manière statique (à partir d'une chaîne) ou via un flux.
Matrice de référence des API
| Action | Statique (sûr) | Streaming (Safe) | Statique (non fiable) | Streaming (non sécurisé) |
|---|---|---|---|---|
| Ensemble | setHTML() |
streamHTML() |
setHTMLUnsafe() |
streamHTMLUnsafe() |
| Remplacer | replaceWithHTML() |
streamReplaceWithHTML() |
replaceWithHTMLUnsafe() |
streamReplaceWithHTMLUnsafe() |
| Avant | beforeHTML() |
streamBeforeHTML() |
beforeHTMLUnsafe() |
streamBeforeHTMLUnsafe() |
| Ajouter au début | prependHTML() |
streamPrependHTML() |
prependHTMLUnsafe() |
streamPrependHTMLUnsafe() |
| Ajouter | appendHTML() |
streamAppendHTML() |
appendHTMLUnsafe() |
streamAppendHTMLUnsafe() |
| Après | afterHTML() |
streamAfterHTML() |
afterHTMLUnsafe() |
streamAfterHTMLUnsafe() |
Concepts clés
- Sûr ou non sûr :
- Les méthodes Safe (par exemple,
setHTML()) utilisent par défaut un outil de nettoyage intégré pour supprimer les contenus potentiellement dangereux, tels que les balises<script>. Vous pouvez transmettre un objetSanitizerpersonnalisé dans les options à appliquer en plus du nettoyeur par défaut. - Les méthodes non sécurisées (par exemple,
setHTMLUnsafe()) n'effectuent pas de nettoyage par défaut, mais vous pouvez toujours transmettre unSanitizerpersonnalisé. Pour autoriser l'exécution de scripts dans le nouveau contenu, vous devez transmettre explicitement{ runScripts: true }dans les options. IMPORTANT : N'utilisez des méthodes non sécurisées qu'avec du contenu de confiance lorsque les méthodes sécurisées ne fonctionnent pas.
- Les méthodes Safe (par exemple,
- Statique ou streaming :
- Les méthodes statiques prennent une chaîne (ou
TrustedHTML) et l'appliquent immédiatement. - Les méthodes de streaming renvoient un
WritableStream. Cela vous permet de transférer du contenu (par exemple, à partir d'une réponsefetch) directement dans le DOM, et le navigateur l'affichera de manière incrémentielle à mesure que les fragments arrivent.
- Les méthodes statiques prennent une chaîne (ou
Exemple : Diffuser une réponse Fetch en streaming
Vous pouvez rediriger les réponses de récupération directement vers le DOM sans gestion manuelle des fragments.
const main = document.querySelector('main');
const response = await fetch('/api/partial-update');
// Pipe the stream directly into the element
await response.body
.pipeThrough(new TextDecoderStream())
.pipeTo(main.streamHTMLUnsafe({ runScripts: true }));
// Or use the `textStream()` convenience method which streams directly without needing the intermediate `TextDecoderStream()` step
await response
.textStream()
.pipeTo(main.streamHTMLUnsafe({ runScripts: true }))
Matrice de référence des cas d'utilisation
- Architecture en îlots : hydratez des composants interactifs indépendants au-dessus du code HTML statique sans environnement d'exécution de framework complet.
- Blocs "coûteux" différés : diffusez immédiatement l'interface d'une page et diffusez en streaming les composants lourds de la base de données dès qu'ils sont disponibles.
- Mises à jour des applications monopages (SPA) : diffusez des transitions d'UI volumineuses dans les SPA à l'aide de
streamHTML()au lieu d'attendre une charge utile JSON complète et une construction manuelle du DOM.
Antimodèles et avertissements (À NE PAS FAIRE)
- N'utilisez pas le streaming OOO pour tout. Une fragmentation excessive d'une page peut augmenter la complexité du flux et potentiellement retarder le contenu si le navigateur passe trop de temps à corriger de petits nœuds.
- N'ignorez pas les Trusted Types. Lorsque vous utilisez des méthodes
*Unsafedans des environnements où les Trusted Types sont appliqués, assurez-vous de transmettre un objet de stratégie nettoyé si nécessaire. - Ne présumez pas de l'ordre d'exécution des scripts. Les scripts d'un
<template for>s'exécutent dès qu'ils sont intégrés au document. Ne vous fiez pas à leur exécution dans un ordre spécifique par rapport aux autres modèles différés. - Ne l'utilisez pas sans stratégie de polyfill. Assurez-vous de disposer d'une solution de remplacement ou d'utiliser des polyfills (
html-setters-polyfillettemplate-for-polyfill) pour prendre en charge les navigateurs requis jusqu'à ce que cette fonctionnalité devienne Baseline.
Stratégies de remplacement
Vous pouvez utiliser des polyfills tels que template-for-polyfill et html-setters-polyfill pour émuler la surface de l'API pour les navigateurs non compatibles.
Les navigateurs qui ne sont pas compatibles avec html-processing-instructions analysent <?marker>, <?start> et <?end> en tant que commentaires. template-for-polyfill reconnaît toujours ces commentaires comme des repères. Conservez donc la même syntaxe d'instruction de traitement pour tous les navigateurs. Ne réécrivez pas les repères sous forme de commentaires ou d'éléments HTML pour les navigateurs non compatibles.