Comment afficher un Markdown en streaming depuis des réseaux neuronaux sans scintillement ni gel
Lorsque vous connectez pour la première fois la sortie en streaming d'un modèle de langage au frontend, un moteur de rendu Markdown classique transforme presque immédiatement la page en cauchemar. Les bibliothèques standard comme markdown-it ou marked ont été conçues pour des documents statiques prêts à l'emploi. Si vous leur envoyez un flux brut de jetons via SSE ou WebSocket, elles ré-analysent l'ensemble du texte à chaque fragment, redessinent l'arbre DOM et perturbent le défilement.
À ce stade, l'interface commence à scintiller de manière notable. La coloration syntaxique clignote, les blocs de code inachevés cassent le balisage en dessous, et les formules non fermées restent bloquées dans un état de chargement sans fin. Le dépôt markstream-vue résout ce problème exact et frustrant.
Ce qui se cache sous le capot de la bibliothèque
Le projet a commencé comme un composant spécialisé pour Vue 3, mais avec le temps, l'auteur a split l'architecture en un cœur stream-markdown-parser et des adaptateurs pour différents frameworks. Il existe désormais des packages prêts à l'emploi pour Vue 3, Nuxt, React, Next.js, Svelte 5, Angular et même le legacy Vue 2.
La tâche principale du moteur de rendu est de maintenir la stabilité du DOM lors des fréquentes micro-mises à jour du texte. La bibliothèque analyse le flux de manière incrémentale, comprend les états intermédiaires des balises non fermées, et met à jour uniquement les nœuds modifiés, laissant le reste de la page intact.
Modes de fonctionnement et gestion de la charge
La bibliothèque propose deux approches de rendu fondamentalement différentes qui peuvent être commutées via la prop mode.
Le mode mode="chat" est conçu pour les chats IA. Dans celui-ci, le moteur de rendu groupe les jetons entrants en petits lots et les affiche avec un effet de saisie fluide. Dans le même temps, les animations d'opacité inutiles sont désactivées afin que l'interface ne tremble pas à chaque nouveau mot.
Si vous devez afficher un long article ou une documentation générée volumineux, il est préférable d'activer la virtualisation via mode="docs". Le moteur de rendu maintient une fenêtre fixe d'éléments dans l'arbre DOM actif (environ 220 nœuds par défaut). Cela maintient la consommation de mémoire du navigateur à un niveau stable et empêche les gels lors du défilement dans de longues conversations.
<script setup lang="ts">
import { ref } from 'vue'
import MarkdownRender from 'markstream-vue'
import 'markstream-vue/index.css'
const message = ref('')
const isDone = ref(false)
// Получаем чанки через EventSource или fetch
const eventSource = new EventSource('/api/chat')
eventSource.onmessage = (event) => {
message.value += event.data
}
eventSource.addEventListener('done', () => {
isDone.value = true
eventSource.close()
})
</script>
<template>
<MarkdownRender
mode="chat"
:content="message"
:final="isDone"
smooth-streaming="auto"
:fade="false"
/>
</template>
La prop final est critique lors du travail avec un flux. Tant que final="false" est vrai, le parseur tolère calmement les constructions coupées à mi-mot. Dès que le signal de complétion arrive, le moteur de rendu vide le cache de streaming et ramène le balisage à sa forme finale.
Travailler avec des blocs complexes
Les parseurs réguliers trébuchent sur les diagrammes Mermaid ou les formules KaTeX si la syntaxe n'a pas encore été complètement écrite. Dans markstream, ce moment est pensé dans les moindres détails.
Diagrammes Mermaid et formules
Les dépendances lourdes comme mermaid et katex ne sont pas incluses dans le bundle principal. Vous les installez en tant que peer dependencies et les activez en appelant des fonctions :
import { enableKatex, enableMermaid } from 'markstream-vue'
import 'katex/dist/katex.min.css'
enableMermaid()
enableKatex()
Les diagrammes Mermaid sont analysés de manière progressive. Si le graphe est encore en cours de rédaction par le modèle, le moteur de rendu affiche un placeholder soigné au lieu d'une erreur de syntaxe dans la console. Pour KaTeX, vous pouvez décharger l'analyse des formules vers un Web Worker séparé via CDN, afin que les expressions mathématiques lourdes ne bloquent pas du tout le thread principal de l'interface.
Blocs de code et diffs
Dans la version 2.0, les développeurs ont abandonné le lourd éditeur Monaco au profit de l'intégration avec stream-diffs. Vous pouvez désormais afficher des diffs de fichiers interactives directement dans le flux, basculer entre les thèmes clair et sombre, et configurer les hauteurs des blocs.
<template>
<MarkdownRender
:content="content"
:is-dark="true"
:code-block-props="{
theme: { light: 'vitesse-light', dark: 'vitesse-dark' }
}"
/>
</template>
Composants Vue personnalisés dans Markdown
Parfois, les modèles sortent des balises non standard, par exemple <thinking> pour une chaîne de raisonnement ou des shortcodes personnalisés pour appeler des boutons et widgets. Vous pouvez intercepter ceux-ci et les remplacer par des composants Vue complets :
import { setCustomComponents } from 'markstream-vue'
setCustomComponents('chat-scope', {
CALLOUT: () => import('./components/Callout.vue'),
THINKING: () => import('./components/ThinkingAccordion.vue'),
})
Dans le template, vous devez simplement spécifier le même identifiant :
<MarkdownRender
:content="message"
custom-id="chat-scope"
:custom-html-tags="['thinking']"
/>
Rendu côté serveur et transfert d'état
Si vous construisez une application avec Nuxt ou Next.js, vous n'avez pas besoin d'exécuter le parseur côté client depuis zéro. Le document peut être analysé sur le serveur en une structure de nœuds typés :
import { getMarkdown, parseMarkdownToStructure } from 'markstream-vue'
const md = getMarkdown()
const nodes = parseMarkdownToStructure(rawMarkdown, md, { final: true })
Le composant client accepte les nœuds prêts via la prop :nodes="nodesFromServer", ce qui fournit une hydratation rapide sans décalage de mise en page. Si vous devez continuer le streaming après le chargement initial de la page, le client reprend simplement le buffer et analyse les nouvelles portions.
Scénarios pratiques
La bibliothèque couvre plusieurs tâches courantes du développement frontend en même temps :
- Interfaces de dialogue avec de grands modèles de langage, où il est important d'éliminer le scintillement et les tremblements d'écran.
- Systèmes de révision de code et de génération de patches avec affichage des diffs directement pendant la génération.
- Bases de connaissances et panneaux de changelog avec chargement dynamique de sections et composants interactifs.
- Pages de documentation technique avec formules et diagrammes complexes.
Résumé
Si votre projet affiche des fichiers Markdown statiques depuis un dossier local, un markdown-it éprouvé gérera cela sans complications inutiles. Mais si vous travaillez avec un flux de jetons en direct depuis un LLM, si vous déplacez une interface de chat vers le web, ou si vous êtes fatigué de lutter contre le lag lors du rendu de longues réponses, la bibliothèque mérite définitivement une place dans vos dépendances.
Elle élimine des dizaines de problèmes de streaming non évidents et fait gagner beaucoup de temps sur l'écriture de vos propres solutions de contournement autour des parseurs. Pour un démarrage rapide, vous pouvez consulter le playground en ligne officiel ou déployer un environnement de test via StackBlitz.
Projets similaires