Parte 3 de Pretext: La Librería de 15 kb Que Esquiva la Operación Más Cara del Navegador
El Demo
Pulsa Simulate Stream para ver llegar mensajes de IA token a token. Cada burbuja se expande fluidamente mientras se llena de texto — sin jank, sin reflow, sin layout thrashing. Luego cambia al modo 🐢 DOM y ejecútalo de nuevo para ver la diferencia en el contador de fps.
Loading pretext…
⚡ pretext — arithmetic layout, zero DOM reads per frame
Qué Está Pasando
En modo ⚡ pretext, cada fotograma hace esto:
- Obtiene el fragmento de texto actual en streaming
- Llama a
prepare(text, font)— medición en canvas, se ejecuta una vez cuando el texto cambia - Llama a
layout(prepared, bubbleWidth, lineHeight)— aritmética pura, devuelve la altura - Establece
style.heighten la burbuja
Sin lecturas del DOM después del paso 2. El árbol de layout del navegador nunca se ensucia por la medición en sí.
En modo 🐢 DOM, cada fotograma hace esto en su lugar:
- Obtiene el fragmento de texto actual en streaming
- Crea un
<div>oculto, establece su texto y estilos, lo agrega adocument.body - Llama a
getBoundingClientRect()— fuerza reflow de layout síncrono - Elimina el elemento oculto
- Establece
style.heighten la burbuja
El reflow forzado en el paso 3 bloquea el hilo principal. Con múltiples mensajes en streaming simultáneamente, los reflows se acumulan.
El Patrón Central
import { prepare, layout } from '@chenglou/pretext'
function measureBubbleHeight(text: string, maxWidth: number): number { const prepared = prepare(text, '14px ui-sans-serif, system-ui, sans-serif') const { height } = layout(prepared, maxWidth, 22) return height + 24 // agregar padding}
// Dentro de tu manejador de actualización en streaming:const height = measureBubbleHeight(currentText, bubbleWidth)element.style.height = `${height}px`En producción cachearías el identificador PreparedText entre ticks de streaming — solo llamas a prepare() cuando el texto completo cambia significativamente, no en cada token. La llamada a layout() es la barata; es la que llamas por fotograma.
Manejando Múltiples Streams Simultáneos
Donde las ganancias se multiplican es cuando varios mensajes están en streaming al mismo tiempo. Pretext hace que cada uno sea independiente:
// Todo esto corre en el mismo animation frame sin reflowfor (const message of streamingMessages) { const { height } = layout(message.prepared, containerWidth, LINE_HEIGHT) message.element.style.height = `${height}px`}Con medición DOM, cada acceso a un elemento forzaría un reflow que potencialmente invalida las mediciones que acabas de calcular para los elementos anteriores. Con pretext, todas las mediciones son operaciones aritméticas independientes sobre datos cacheados.
El Caso pre-wrap
Las entradas tipo textarea necesitan white-space: pre-wrap para respetar los saltos de línea duros y tabulaciones:
import { prepare, layout } from '@chenglou/pretext'
const prepared = prepare(textareaValue, '14px ui-monospace', { whiteSpace: 'pre-wrap' })const { height } = layout(prepared, textareaWidth, 20)Esto es útil para textareas que crecen automáticamente — uno de los patrones más comunes que tradicionalmente requería loops de medición DOM.
Siguiente: Matteflow — texto fluyendo alrededor de un bailarín →