La construcción de un editor de Markdown es un proyecto fundamental para comprender la reactividad y la optimización de eventos en aplicaciones modernas. Utilizando Vue 3 y su Composition API, es posible implementar una interfaz fluida que procese texto enriquecido de manera eficiente.
Arquitectura Técnica
Para este desarrollo, se seleccionan herramientas específicas que garantizan un rendimiento óptimo:
- Vue 3 (script setup): Manejo del estado mediante
refy propiedades calculadas (computed). - Marked.js: Un motor de parsing de alto rendimiento para convertir sintaxis Markdown en HTML.
- Lodash (Debounce): Técnica de control para limitar la frecuencia de ejecución de funciones pesadas durante la entrada de usuario.
Optimización del Procesamiento mediante Debouncing
Uno de los desafíos principales al renderizar Markdown en tiempo real es el costo computacional de transformar el texto en cada pulsación de tecla. Si el usuario escribe rápidamente, la actualización constante de la vista previa puede degradar la experiencia de usuario (UX).
La solución consiste en implementar un retraso controlado. En lugar de actualizar la fuente de datos inmediatamente, empleamos una función debounce que espera a que el usuario deje de escribir durante un periodo determinado (por ejemplo, 200ms) antes de procesar el cambio.
import { debounce } from 'lodash-es';
// Función para actualizar el estado con retraso
const handleInput = debounce((event) => {
content.value = event.target.value;
}, 200);
Implementación del Componente
El diseño se basa en un sistema de doble panel: una zona de entrada de texto y una zona de visualización. La reactividad de Vue se encarga de sincronizar ambos estados a través de una propiedad computada que invoca al compilador de Markdown.
<script setup>
import { ref, computed } from 'vue';
import { marked } from 'marked';
import { debounce } from 'lodash-es';
// Estado reactivo inicial
const sourceText = ref('# Nuevo documento\nEmpieza a escribir...');
// Transformación de Markdown a HTML mediante computed
const htmlPreview = computed(() => marked.parse(sourceText.value));
// Gestión de eventos con debounce para mejorar el rendimiento
const syncContent = debounce((e) => {
sourceText.value = e.target.value;
}, 150);
</script>
<template>
<main class="editor-container">
<textarea
class="source-editor"
:value="sourceText"
@input="syncContent"
placeholder="Escribe tu código Markdown aquí..."
></textarea>
<section
class="preview-pane"
v-html="htmlPreview"
></section>
</main>
</template>
<style scoped>
.editor-container {
display: flex;
height: 100vh;
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
}
.source-editor, .preview-pane {
flex: 1;
height: 100%;
padding: 25px;
box-sizing: border-box;
overflow-y: auto;
}
.source-editor {
border: none;
border-right: 1px solid #ddd;
background-color: #fafafa;
resize: none;
outline: none;
font-family: 'Fira Code', monospace;
font-size: 1rem;
color: #2c3e50;
}
.preview-pane {
background-color: #ffffff;
line-height: 1.6;
}
:deep(pre) {
background-color: #f1f1f1;
padding: 10px;
border-radius: 4px;
}
</style>
Flujo de Datos
- El usuario interactúa con el elemento
textarea. - El evento
@inputes capturado por la funciónsyncContent, que está protegida por el debounce. - Una vez trnascurrido el tiempo de espera, la variable reactiva
sourceTextse actualiza. - La propiedad computada
htmlPreviewdetecta el cambio en su dependencia y ejecutamarked.parse(). - Vue actualiza el DOM de forma segura mediante la directiva
v-htmlen el panel de previsualización.