Come eseguire il rendering di Markdown in streaming dalle reti neurali senza sfarfallii e blocchi
Quando colleghi per la prima volta l'output in streaming da un modello linguistico al frontend, un normale renderer Markdown trasforma quasi immediatamente la pagina in un incubo. Librerie standard come markdown-it o marked sono state progettate per documenti statici pronti. Se le alimenti con un flusso di token grezzi tramite SSE o WebSocket, riparano l'intero testo a ogni chunk, ridisegnano l'albero DOM e interferiscono con lo scrolling.
A questo punto, l'interfaccia inizia a sfarfallare in modo evidente. L'evidenziazione della sintassi lampeggia, i blocchi di codice non completati rompono il markup sottostante e le formule non chiuse rimangono bloccate in uno stato di caricamento infinito. Il repository markstream-vue risolve esattamente questo problema frustrante.
Cosa c'è sotto il cofano della libreria
Il progetto è nato come componente specializzato per Vue 3, ma nel tempo l'autore ha suddiviso l'architettura in un core stream-markdown-parser e adapter per diversi framework. Ora ci sono pacchetti pronti all'uso per Vue 3, Nuxt, React, Next.js, Svelte 5, Angular e persino il legacy Vue 2.
Il compito principale del renderer è mantenere il DOM stabile durante frequenti micro-aggiornamenti del testo. La libreria analizza il flusso in modo incrementale, comprende gli stati intermedi dei tag non chiusi e aggiorna solo i nodi modificati, lasciando intatta la parte rimanente della pagina.
Modalità operative e gestione del carico
La libreria offre due approcci di rendering fondamentalmente diversi che possono essere commutati tramite la prop mode.
La modalità mode="chat" è pensata per le chat AI. In questa modalità, il renderer raggruppa i token in arrivo in piccoli batch e li emette con un effetto di digitazione fluido. Allo stesso tempo, le animazioni di opacità non necessarie vengono disattivate per evitare che l'interfaccia tremi a ogni nuova parola.
Se hai bisogno di visualizzare un lungo documento generato o una documentazione di grandi dimensioni, è meglio abilitare la virtualizzazione tramite mode="docs". Il renderer mantiene una finestra fissa di elementi nell'albero DOM attivo (circa 220 nodi per impostazione predefinita). Questo mantiene il consumo di memoria del browser a un livello costante e previene i blocchi durante lo scorrimento di conversazioni lunghe.
<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 è fondamentale quando si lavora con uno stream. Finchè final="false" è true, il parser tollera serenamente costrutti troncati a metà parola. Quando arriva il segnale di completamento, il renderer svuota la cache di streaming e porta il markup alla sua forma definitiva.
Lavorare con blocchi complessi
I parser tradizionali inciampano sui diagrammi Mermaid o sulle formule KaTeX quando la sintassi non è ancora completamente scritta. In markstream, questo momento è stato pensato nei minimi dettagli.
Diagrammi Mermaid e formule
Le dipendenze pesanti come mermaid e katex non sono incluse nel bundle principale. Le installi come dipendenze peer e le attivi chiamando le funzioni:
import { enableKatex, enableMermaid } from 'markstream-vue'
import 'katex/dist/katex.min.css'
enableMermaid()
enableKatex()
I diagrammi Mermaid vengono analizzati in modo progressivo. Se il grafico è ancora in fase di scrittura da parte del modello, il renderer mostra un placeholder ordinato invece di un errore di sintassi nella console. Per KaTeX, puoi scaricare l'analisi delle formule su un Web Worker separato tramite CDN, così le espressioni matematiche complesse non bloccano affatto il thread principale dell'interfaccia.
Blocchi di codice e diff
Nella versione 2.0, gli sviluppatori hanno abbandonato l'editor Monaco pesante a favore dell'integrazione con stream-diffs. Ora puoi visualizzare diff di file interattivi direttamente nello stream, passare tra temi chiari e scuri e configurare le altezze dei blocchi.
<template>
<MarkdownRender
:content="content"
:is-dark="true"
:code-block-props="{
theme: { light: 'vitesse-light', dark: 'vitesse-dark' }
}"
/>
</template>
Componenti Vue personalizzati all'interno di Markdown
A volte i modelli emettono tag non standard, ad esempio <thinking> per una catena di ragionamento o shortcode personalizzati per chiamare pulsanti e widget. Puoi intercettarli e sostituirli con componenti Vue completi:
import { setCustomComponents } from 'markstream-vue'
setCustomComponents('chat-scope', {
CALLOUT: () => import('./components/Callout.vue'),
THINKING: () => import('./components/ThinkingAccordion.vue'),
})
Nel template hai solo bisogno di specificare lo stesso identificatore:
<MarkdownRender
:content="message"
custom-id="chat-scope"
:custom-html-tags="['thinking']"
/>
Rendering lato server e trasferimento di stato
Se stai costruendo un'app con Nuxt o Next.js, non devi eseguire il parser sul client da zero. Il documento può essere analizzato sul server in una struttura di nodi tipizzati:
import { getMarkdown, parseMarkdownToStructure } from 'markstream-vue'
const md = getMarkdown()
const nodes = parseMarkdownToStructure(rawMarkdown, md, { final: true })
Il componente client accetta nodi pronti tramite la prop :nodes="nodesFromServer", che fornisce un'hydration rapida senza mismatch di layout. Se hai bisogno di continuare lo streaming dopo il caricamento iniziale della pagina, il client semplicemente riprende il buffer e analizza nuove porzioni.
Scenari pratici
La libreria copre diversi compiti comuni di sviluppo frontend contemporaneamente:
- Interfacce di dialogo con modelli linguistici di grandi dimensioni, dove è importante eliminare sfarfallii e tremori dello schermo.
- Sistemi di revisione del codice e generazione di patch con visualizzazione delle diff durante la generazione.
- Knowledge base e pannelli di changelog con caricamento dinamico delle sezioni e componenti interattivi.
- Pagine di documentazione tecnica con formule e diagrammi complessi.
Riepilogo
Se il tuo progetto visualizza file Markdown statici da una cartella locale, un markdown-it collaudato lo gestirà senza complicazioni inutili. Ma se stai lavorando con un flusso di token live da un LLM, spostando un'interfaccia di chat sul web, o sei stanco di combattere il ritardo durante il rendering di risposte lunghe, la libreria merita sicuramente un posto nelle tue dipendenze.
Elimina dozzine di problemi di streaming non ovvi e fa risparmiare un sacco di tempo sulla scrittura di workaround personalizzati attorno ai parser. Per un avvio rapido, puoi consultare il playground online ufficiale o distribuire un ambiente di test tramite StackBlitz.
Progetti correlati