Preview, Chrome veut changer l'ordre du HTML

Posté le - Astuces pour développeurs Par Grafikart

Le streaming HTML permet déjà au navigateur d'afficher une page progressivement au fil du chargement du document. Le problème, c'est que ce flux reste linéaire : si un bloc au milieu de la page met du temps à être généré, tout ce qui arrive après est bloqué. Chrome expérimente une nouvelle approche avec le Declarative partial update, qui permet d'injecter des morceaux d'HTML à des emplacements précis et ainsi de charger la page dans le désordre.

Cette fonctionnalité est encore expérimentale et nécessite une version récente de Chrome avec un drapeau spécifique. Elle n'est pas standardisée et peut donc changer ou disparaître. Cependant, je voulais en parler, car il s'agit d'une proposition de changement importante pour l'un des éléments fondamentaux du Web : l'HTML.

Le problème du streaming HTML classique

Quand une page HTML est streamée, le navigateur commence à afficher les premiers éléments sans attendre que tout le document soit disponible. C'est pratique pour donner rapidement du contenu à l'utilisateur, mais ce chargement se fait dans l'ordre du document. Si une section intermédiaire est lente à générer, le reste de la page doit attendre, même si cette section n'est pas forcément importante pour l'utilisateur. L'idée proposée par Chrome est de pouvoir placer un marqueur dans la page, puis d'envoyer plus tard un morceau d'HTML qui viendra remplacer ce marqueur.

Injecter un contenu avec un marqueur

Le principe repose sur deux éléments :

<!-- Marker qui accueillera notre contenu --> <?marker name="gallery"> <!-- Plus loin dans le flux HTML --> <template for="gallery"> <section> <!-- Contenu de la galerie --> </section> </template>

Avec cette approche, le navigateur peut afficher le reste de la page immédiatement. Lorsque le template arrive dans le flux, son contenu est injecté à l'emplacement du marqueur.

Ajouter un placeholder pendant le chargement

Il existe aussi une syntaxe alternative permettant d'associer du contenu aux marqueurs. L'intérêt est de pouvoir afficher un contenu temporaire pendant le chargement.

<?start name="gallery"> Chargement de la galerie... <?end gallery>

Ce contenu peut être du texte ou une structure HTML. Quand le template correspondant arrive, le placeholder est remplacé par le contenu final.

Charger plusieurs éléments progressivement

Le système peut aussi servir à charger une liste d'éléments petit à petit. Par exemple, si chaque image d'une galerie met une seconde à être générée côté serveur, on peut streamer chaque image dans un template. Il faut cependant faire attention à un détail : lorsqu'un template est injecté dans un marqueur, le marqueur est consommé. Si plusieurs templates ciblent le même marqueur, seul le premier sera injecté si aucun nouveau marqueur n'est recréé.

Pour permettre un chargement progressif, chaque template peut donc réinsérer un marqueur du même nom après son contenu.

<?marker name="image"> <template for="image"> <img src="image-1.jpg" alt="" /> <?marker name="image"> </template> <template for="image"> <img src="image-2.jpg" alt="" /> <?marker name="image"> </template>

À chaque nouvelle image reçue, le navigateur l'insère puis retrouve un marqueur disponible pour l'image suivante. On obtient ainsi un chargement progressif des images.

Utiliser le streaming depuis JavaScript

Cette logique ne se limite pas au premier chargement de la page. Elle peut aussi s'appliquer lorsqu'on récupère du contenu avec JavaScript.

Au lieu de récupérer du JSON puis de reconstruire l'HTML côté client, on peut demander au serveur de renvoyer directement des fragments HTML contenant des templates. Le flux de la réponse est ensuite branché sur un élément de la page.

const response = await fetch("/api/content"); response.body .pipeThrough(new TextDecoderStream()) .pipeThrough(document.body.streamAppendHTMLUnsafe());

La méthode streamAppendHTMLUnsafe() permet ensuite d'ajouter l'HTML reçu dans le body sous forme de stream (la version unsafe n'échappe pas l'HTML, contrairement à la version non unsafe). Si notre API met du temps à produire tous les éléments, les premiers fragments peuvent être injectés dès qu'ils arrivent.

Cette approche permet d'envoyer uniquement les morceaux d'HTML qui doivent changer, plutôt qu'une page complète. Le concept existe déjà dans des outils comme htmx ou unpoly, mais l'intérêt ici serait d'avoir un mécanisme géré nativement par le navigateur.

Cela pourrait aussi servir à simuler une navigation de type SPA : le JavaScript intercepte les clics sur les liens, appelle le serveur avec des indicateurs spécifiques, puis le serveur renvoie uniquement les templates nécessaires pour mettre à jour la page à l'aide de <template>.

Charger un template depuis une URL

Un autre point évoqué dans l'article est la possibilité d'associer une URL à un template. L'idée ressemble un peu à une iframe au premier abord, mais sans l'isolation qui l'accompagne.

<template for="footer" patchsrc="/partials/footer.html"></template>

Le contenu chargé serait injecté directement dans la page courante. Il pourrait donc profiter des styles, du JavaScript et des custom elements déjà présents sur la page. Cela ouvre des possibilités intéressantes pour les morceaux d'interface que l'on souhaite charger dans un second temps, comme un formulaire qui ne serait nécessaire que lorsqu'il devient visible.

Limites et cas d'usage

Cette fonctionnalité ouvre des perspectives intéressantes, mais elle ne conviendra pas forcément à toutes les architectures. Dans une structure MVC classique, on récupère souvent toutes les données avant de générer la vue. Si la lenteur vient de la récupération des données en amont, on n'a pas toujours d'HTML à envoyer en attendant.

Le bénéfice semble plus évident dans des approches où la logique est proche des composants (comme avec des server components, par exemple). De mon côté, je trouve que c'est une idée intéressante, mais elle présente certains points de vigilance :